Custom Component for printer ink levels

Trying to get this working on my mx922. Looks good in the add-on log:

nk 0.5.3 (c) 2018 Markus Heinz
Canon MX920 series
Black:                                  60%
Photoblack:                             80%
Yellow:                                 80%
Magenta:                                70%
Cyan:                                   70%
-------------------------------------------
Parameters:
MQTT Host = 192.168.1.9
Printer IP address = 192.168.1.50
Sleep interval = 3600
Brand =  Canon
Type =  MX920

But when I try to add the sensors, the config checker gives me:

Platform error sensor.ink - No module named 'custom_components.ink.sensor'

What files/folders should I have under custom_components ?

@bmorgan @febalci Any idea if it’s possible with my model?
Hi This is my printer : hp officejet 3830
print screen below:

Can you help with the code?

My envy looks the same, would be difficult I think

The supported printers for the ‘ink’ command is: http://libinklevel.sourceforge.net/#supported And i see a HP Deskjet 3820 there… Feels like it could also have support for 3830. Have you tried it?

What does the ink command actually do?

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: