-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprintermonitor.ppy
executable file
·241 lines (220 loc) · 9.99 KB
/
printermonitor.ppy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# Printermonitor -
# Monitor printers for low ink or toner level
# Written by Chip Cox
# Date 16Jan2017
##################################
#
# 27JAN2017 - updated to handle bulk snmp reads to take care of magenta issue
#
##################################
"""
Requires
A. pysnmp sudo pip install pysnmp
I think this is installed with the SNMP component in HA, but just in case:
sudo pip install pysnmp
Installation
1. Copy this file our appdaemon application directory
2. Make input_numbers in your ha configuration.yaml file as follows
A. input_number.Printername_markercolor
for example
input_number.oshp1_black # happens to be a monochrome laserjet
input_number.dsp1hp_black # inkjet color printer black cartridge
input_number.dsp1hp_yellow # inkjet color printer yellow cartridge
input_number.dsp1hp_cyan # inkjet color printer cyan cartridge
input_number.dsp1yp_magenta # inkjet color printer magenta cartridge
B. each input slider should have
min value of 0
max value of 100
initial value of 0
C. setup a group for each printer and put the input sliders associated with that
printer in the group. Use the printer name in the config file to associate the
group with the IP address for the printer.
D. in your appdaemon.cfg file
[printermonitor]
module = printermonitor
class = printermonitor
community = <read only community name> defaults to public
PrinterAddresses = ["192.168.2.247","192.168.2.249"]
PrinterGroups = ["group.entity_dsp1hp","group.entity_ofhp1"]
E. Restart HA
"""
##################################
import appdaemon.plugins.hass.hassapi as hass
import datetime
import time
from pysnmp.hlapi import *
m4_include(/home/appdaemon/code/appdaemon/adlib_imports.pyi)
class printermonitor(hass.Hass):
#######################
#
# Initialize (not much to do here
#
########################
def initialize(self):
self.host_name_odi="1.3.6.1.2.1.1.5"
self.printer_name_odi='1.3.6.1.2.1.1.5.0'
self.marker_base_odi="1.3.6.1.2.1.43.11.1.1"
self.marker_name_suffix="6"
self.marker_capacity_suffix="8"
self.marker_current_level_suffix="9"
self.showpct=True
if "showpct" in self.args:
self.showpct=eval(self.args["showpct"])
if "community" in self.args:
self.community=self.args["community"]
else:
self.log("community not provided in appdaemon.cfg file, defaulting to public")
self.community="public"
self.log("Community set to {}".format(self.community))
self.check_printers()
self.log("check_printers has run")
self.run_every(self.hourly_check_handler,self.now(),5*60)
#self.run_every(self.hourly_check_handler,datetime.now(),5*60)
self.log("initialization compleete")
#######################
#
# run_every handler to check to see if the print status has changed.
#
#######################
def hourly_check_handler(self,kwargs):
self.check_printers()
########################
#
# main process to check the printers and update their status in HA
#
########################
def check_printers(self):
paddrlist=eval(self.args["printeraddresses"]) # get ip addresses from appdaemon.cfg file
pgrouplist=eval(self.args["printergroups"])
#self.log("ipalist={}".format(ipalist))
ipalist=[]
for i in range(0,len(paddrlist)):
ipalist.append((paddrlist[i],pgrouplist[i]))
#self.log("ipalist={}".format(ipalist))
for ipa,pgroup in ipalist: # loop through addresses in list
self.log("looking up printer {}".format(ipa))
result={}
# because we are using nextcmd we are starting with an odi one level prior to what we need
hostname=self.getsnmptree(ipa,self.host_name_odi)
result=self.getsnmptree(ipa,self.marker_base_odi)
num_markers=0
for mkrs in result:
self.log("mkrs={}".format(mkrs))
namebase=mkrs.find(self.marker_base_odi+"."+self.marker_name_suffix)
strangevalue=mkrs[len(self.marker_base_odi+"."+self.marker_name_suffix)+1:][:1]
#self.log("strangevalue={}".format(strangevalue))
if namebase>=0:
num_markers=num_markers+1
self.log("num_markers={}".format(num_markers))
#num_markers=0
#if len(result)%4==0:
# num_markers=4
#else:
# num_markers=1
if result=={}:
self.log("printer ipa={} not responding".format(ipa))
continue
result.update(hostname) # combine everything together in one dictionary
printername=result[self.printer_name_odi].strip().lower()
self.log("hostname={}".format(printername))
#num_markers=int((len(result)-1)/8) # there are 8 attributes for each printer
low=False # we are not low on ink or toner
for i in range (1, num_markers+1): # loop through markers could be ink or toner
tail="."+strangevalue+"."+str(i)
#This is just to document the information
#
#black ink is 97.98% full
#yellow ink is 99.12% full
#cyan ink is 99.15% full
#magenta ink is 99.20% full
#Black Cartridge HP CE278A is 46.00% full
#
# This is what our input sliders are called
#- input_number.dsp1hp_black
#- input_number.dsp1hp_cyan
#- input_number.dsp1hp_yellow
#- input_number.dsp1hp_magenta
#prtMarkerSupplies 1.3.6.1.2.1.43.11
#prtMarkerSuppliesTable 1.3.6.1.2.1.43.11.1
#prtMarkerSuppliesEntry 1.3.6.1.2.1.43.11.1.1
#prtMarkerSuppliesIndex 1.3.6.1.2.1.43.11.1.1.1 - one entry here
#Each of the following entries is followed by either an indicator of whether
#it's a monocrome printer or color. It will end in a 1.1 if it is monocrome
#if it's color, it will end with a .0. and a range of 1-4 one digit for each color
#so suppliesdescription for a color printer would look similar to this
# 1.3.6.1.2.1.43.11.1.1.6.0.1 black
# 1.3.6.1.2.1.43.11.1.1.6.0.2 Yellow
# 1.3.6.1.2.1.43.11.1.1.6.0.3 Cyan
# 1.3.6.1.2.1.43.11.1.1.6.0.4 Magenta
#prtMarkerSuppliesMarkerIndex 1.3.6.1.2.1.43.11.1.1.2
#prtMarkerSuppliesColorantIndex 1.3.6.1.2.1.43.11.1.1.3
#prtMarkerSuppliesClass 1.3.6.1.2.1.43.11.1.1.4
#prtMarkerSuppliesType 1.3.6.1.2.1.43.11.1.1.5
#prtMarkerSuppliesDescription 1.3.6.1.2.1.43.11.1.1.6
#prtMarkerSuppliesSupplyUnit 1.3.6.1.2.1.43.11.1.1.7
#prtMarkerSuppliesMaxCapacity 1.3.6.1.2.1.43.11.1.1.8
#prtMarkerSuppliesLevel 1.3.6.1.2.1.43.11.1.1.9
# put odi data into variables to make later statements easier to understand and read
markername=result[self.marker_base_odi+"."+self.marker_name_suffix+tail]
markername=markername[:markername.find(" ")].lower().replace("-","_")
markercapacity=int(result[self.marker_base_odi+"."+self.marker_capacity_suffix+tail])
markercurrent=int(result[self.marker_base_odi+"."+self.marker_current_level_suffix+tail])
markerpctfull=int((markercurrent/markercapacity)*100)
if markerpctfull < 10: # < 10% marker means we are low on something
low=True
self.log("{}-{} is {:0.2f}% full".format(self.marker_base_odi+"."+self.marker_name_suffix+tail,
markername,
(markercurrent/markercapacity)*100))
self.log("markerpctfull={}".format(markerpctfull))
# set values for input_numbers
#entity_name="input_number." + printername + "_" + markername
entity_name="sensor." + printername + "_" + markername
self.log("{} = {}".format(entity_name,markerpctfull if markerpctfull>0 else 1))
#self.set_value(entity_name,markerpctfull if markerpctfull>0 else 1)
self.set_state(entity_name,state=markerpctfull if markerpctfull>0 else 1)
self.log("Value set")
frname=self.get_state(entity_name,attribute="friendly_name")
self.log("frname={}".format(frname))
pctdisp=""
if self.showpct:
pctdisp=" - " + str(markerpctfull) + " %"
self.set_state(entity_name,
attributes={"friendly_name":markername+pctdisp})
# outside marker loop, set group state to either low or ok ink levels
#self.log("setting group status to {}".format("Low" if low==True else "Ok"))
#self.set_state(pgroup,"Low" if low==True else "Ok")
#################################
#
# read SNMP data for each ip address and return dictionary of results
#
#################################
def getsnmptree(self,ipaddr,oid):
resultDict={}
# This actually does the snmp reading.
for (errorIndication, # while no errors reading next value
errorStatus,
errorIndex,
varBinds) in nextCmd(SnmpEngine(),
CommunityData(self.community, mpModel=0),
UdpTransportTarget((ipaddr, 161)),
ContextData(),
ObjectType(ObjectIdentity(oid)),
lexicographicMode=False):
# handle errors
if errorIndication:
print(errorIndication)
break
elif errorStatus:
print('%s at %s' % (errorStatus.prettyPrint(),
errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
break
else: # no errors with SNMP lookup
odistr={}
odibind, valuebind=varBinds[0] # start building our dictionary
odistr[str(odibind)]=str(valuebind)
#print("odistr={}".format(odistr))
resultDict.update(odistr)
#print("resultDict={}\n",format(resultDict))
varBinds={}
return(resultDict) # return our dictionary
m4_include(/home/appdaemon/code/appdaemon/adlib.pyi)