Custom Component for printer ink levels

Hi, I also found snmp doesn’t provide you with the ink levels on the OfficeJet 3830.

Not sure how reliable it is at the moment, but I found that the ink percentage levels are exposed in an xml file which can be found on the printer:

ip_address_of_printer/DevMgmt/ConsumableConfigDyn.xml

You’ll find two entries (one for each cartridge)… and it looks something like:

<dd:ConsumablePercentageLevelRemaining>20</dd:ConsumablePercentageLevelRemaining>

In the end, I crudely wrote a python script to query the xml file and pull the values back via a sensor.

config i made in my sensors.yaml:

- platform: command_line
  name: HP Printer Status
  json_attributes:
    - CartridgeLevel0
    - CartridgeLevel1
    - CartridgeColour0
    - CartridgeColour1
  command: 'python3 /config/get_hp_info.py'
  value_template: '{{ value_json.state }}'
  scan_interval: 300

The script I created was (this needs to be put in a file called get_hp_info.py and potentially given execute permissions via chmod). The file needs to be stored in the folder /config/.

#!/usr/bin/python3

import urllib.request
import urllib.parse 
import xml.etree.ElementTree as ET 
from urllib import request 

id_req1 = urllib.request.urlopen('http://xxx.xxx.xxx.xxx/DevMgmt/ConsumableConfigDyn.xml')
id_req2 = urllib.request.urlopen('http://xxx.xxx.xxx.xxx/DevMgmt/ProductStatusDyn.xml')

id_req1_content = id_req1.read()
id_req2_content = id_req2.read()

output = ""

#
# Gather PRODUCT related information i.e. status
#

root2 = ET.fromstring(id_req2_content)

ns2='http://www.hp.com/schemas/imaging/con/ledm/productstatuscategories/2007/10/31'

for index, i in enumerate( root2.findall('.//{%s}StatusCategory' % ns2)):
    if index == 1:
        output = "{ \"state\":\"" + i.text + "\", "
#        print( index,i.text)


#
# Gather CONSUMABLES related information i.e. cartridges (logic below assumes 2 cartridges - otherwise JSON will be badly formatted with missing ,'s)
#

root1 = ET.fromstring(id_req1_content)

ns1='http://www.hp.com/schemas/imaging/con/dictionaries/1.0/'

for index, i in enumerate( root1.findall('.//{%s}ConsumablePercentageLevelRemaining' % ns1)):
    output += "\"CartridgeLevel"+str(index)+"\":\"" + i.text + "\", "
#    print( index, i.text)
for index, i in enumerate( root1.findall('.//{%s}ConsumableLabelCode' % ns1)):
    output += "\"CartridgeColour"+str(index)+"\":\"" + i.text + "\""
    if index == 0:
        output += ", "
#    print( index, i.text)

output += "}"

print(output)

Hope thats useful, I know it’s a hack! :slight_smile:

1 Like

Thank you!!! @jg.balcombe i will try today and i’ll update!!!

Hi. I tired this and see the following in my logs. I placed the sensor code in my configuration.yaml file under sensors and put the script in my scripts.yaml file.

Empty reply found when expecting JSON data

11:39 AM components/command_line/sensor.py (WARNING) - message first occured at 11:29 AM and shows up 3 times

Command failed: python3 /config/get_hp_info.py

11:39 AM components/command_line/sensor.py (ERROR) - message first occured at 11:29 AM and shows up 3 times

Hi… the script I gave you should be stored in a new file called get_hp_info.py and in the same folder as your configuration.yaml. As I use Hassio - I have it stored in /config/

I think you may need execute permissions on the file - so need to issue:

chmod 755 get_hp_info.py

Also make sure you’ve replaced the x’s I put in the script with the ip address of your HP printer. Plese make sure the IP address is static, otherwise the script will fail over time (when the ip address is released).

(I’ve updated my previous instructions to provide some more clarity).

Cool. That worked. Thank you. This is the output

HP Printer Status

HP Printer Status

2 minutes ago

inPowerSave

HP Printer Status

  • Unknown October 27, 2019, 11:19 AM October 27, 2019, 12:21 PM

CartridgeLevel0

20

CartridgeLevel1

80

CartridgeColour0

CMY

CartridgeColour1

K

1 Like

Great news!

cool find, how did you know about the .xml ?

Was playing with how I could use “scrape” to do it, then used Chrome’s inspect tool to see where the data was coming from. Just got lucky.

Theres a few other xml files there as well for other data, which seems to feed the webpage presented when you query the printer.

1 Like

cool, working from the first time
trying to understand the script, why do you need ns1 and ns2 ?

ns1/ns2/ns3 are needed, because the xml file provided is using namespaces (it has dd: as a prefix for the elements) - so you need to define the name space when using findall… Well - that’s what my google search told me! :slight_smile:

ah ok, not sure where you are using ns3 though :slight_smile:

Ah… oops… I found the “Page Count” :slight_smile:

#!/usr/bin/python3

import urllib.request
import urllib.parse 
import xml.etree.ElementTree as ET 
from urllib import request 

id_req1 = urllib.request.urlopen('http://xxx.xxx.xxx.xxx/DevMgmt/ConsumableConfigDyn.xml')
id_req2 = urllib.request.urlopen('http://xxx.xxx.xxx.xxx/DevMgmt/ProductStatusDyn.xml')
id_req3 = urllib.request.urlopen('http://xxx.xxx.xxx.xxx/DevMgmt/ProductUsageDyn.xml')

id_req1_content = id_req1.read()
id_req2_content = id_req2.read()
id_req3_content = id_req3.read()

output = ""

#
# Gather PRODUCT related information i.e. status
#

root2 = ET.fromstring(id_req2_content)

ns2='http://www.hp.com/schemas/imaging/con/ledm/productstatuscategories/2007/10/31'

for index, i in enumerate( root2.findall('.//{%s}StatusCategory' % ns2)):
    if index == 1:
        output = "{ \"state\":\"" + i.text + "\", "
#        print( index,i.text)


#
# Gather PRODUCT Usage related information
#

root3 = ET.fromstring(id_req3_content)

ns3='http://www.hp.com/schemas/imaging/con/dictionaries/1.0/'

for index, i in enumerate( root3.findall('.//{%s}TotalImpressions' % ns3)):
    if index == 0:
        output += "\"PageCount\":\"" + i.text + "\", "
#        print( index,i.text)


#
# Gather CONSUMABLES related information i.e. cartridges (logic below assumes 2 cartridges - otherwise JSON will be badly formatted with missing ,'s)
#

root1 = ET.fromstring(id_req1_content)

ns1='http://www.hp.com/schemas/imaging/con/dictionaries/1.0/'

for index, i in enumerate( root1.findall('.//{%s}ConsumablePercentageLevelRemaining' % ns1)):
    output += "\"CartridgeLevel"+str(index)+"\":\"" + i.text + "\", "
#    print( index, i.text)
for index, i in enumerate( root1.findall('.//{%s}ConsumableLabelCode' % ns1)):
    output += "\"CartridgeColour"+str(index)+"\":\"" + i.text + "\""
    if index == 0:
        output += ", "
#    print( index, i.text)

output += "}"

print(output)

and sensors configuration:

- platform: command_line
  name: HP Printer Status
  json_attributes:
    - CartridgeLevel0
    - CartridgeLevel1
    - CartridgeColour0
    - CartridgeColour1
    - PageCount
  command: 'python3 /config/get_hp_info.py'
  value_template: '{{ value_json.state }}'
  scan_interval: 300
3 Likes

Cool, thnx for sharing… this was my last device on the network that wasn’t integrated in HA :wink:

ha! not surprised, same for me too!

ok, pagecount added also here
thnx :slight_smile:

@jg.balcombe Thank you! it’s working!!!

i have a few questions :
CartridgeLevel0: 90 - is it black ?
CartridgeLevel1: 90 - is it color?
CartridgeColour0: CMY - what is that?
CartridgeColour1: K - what is that?
PageCount: 11 - This i how much pages has been printed?
friendly_name: HP Printer Status

Thank you !!!

  • CartridgeColour0 is the colour for CartridgeLevel0 I believe (CMY = the Colour cartridge)
  • CartridgeColour1 is the colour for CartridgeLevel1 (K = Black? I think - Maybe the XL cartridge may show something else?)
  • PageCount = Yes - seems to be the number of pages printed

Cmy , cyan, magenta, yellow :wink: tricolor indeed

1 Like

Thanks for sharing this. Working fine for my HP Envy 4526.
Was using this MQTT solution, but your solution is way easier and also has the page count :grinning:

1 Like

no probs - happy to share