Tizen 2.1 base
[platform/upstream/hplip.git] / base / status.py
1 # -*- coding: utf-8 -*-
2 #
3 # (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 #
19 # Author: Don Welch, Narla Naga Samrat Chowdary, Yashwant Kumar Sahu
20 #
21
22 from __future__ import division
23
24 # Std Lib
25 import struct
26 import cStringIO
27 import xml.parsers.expat as expat
28 import re
29 import urllib
30 try:
31     from xml.etree import ElementTree
32     etree_loaded = True
33 except ImportError:
34     try:
35         from elementtree.ElementTree import XML
36         elementtree_loaded = True
37     except ImportError:
38         elementtree_loaded = False
39     etree_loaded = False
40
41 # Local
42 from g import *
43 from codes import *
44 import pml, utils
45 import hpmudext
46
47 """
48 status dict structure:
49     { 'revision' :     STATUS_REV_00 .. STATUS_REV_04,
50       'agents' :       [ list of pens/agents/supplies (dicts) ],
51       'top-door' :     TOP_DOOR_NOT_PRESENT | TOP_DOOR_CLOSED | TOP_DOOR_OPEN,
52       'status-code' :  STATUS_...,
53       'supply-door' :  SUPPLY_DOOR_NOT_PRESENT | SUPPLY_DOOR_CLOSED | SUPPLY_DOOR_OPEN.
54       'duplexer' :     DUPLEXER_NOT_PRESENT | DUPLEXER_DOOR_CLOSED | DUPLEXER_DOOR_OPEN,
55       'photo_tray' :   PHOTO_TRAY_NOT_PRESENT | PHOTO_TRAY_ENGAGED | PHOTO_TRAY_NOT_ENGAGED,
56       'in-tray1' :     IN_TRAY_NOT_PRESENT | IN_TRAY_CLOSED | IN_TRAY_OPEN (| IN_TRAY_DEFAULT | IN_TRAY_LOCKED)*,
57       'in-tray2' :     IN_TRAY_NOT_PRESENT | IN_TRAY_CLOSED | IN_TRAY_OPEN (| IN_TRAY_DEFAULT | IN_TRAY_LOCKED)*,
58       'media-path' :   MEDIA_PATH_NOT_PRESENT | MEDIA_PATH_CUT_SHEET | MEDIA_PATH_BANNER | MEDIA_PATH_PHOTO,
59     }
60
61     * S:02 only
62
63 agent dict structure: (pens/supplies/agents/etc)
64     { 'kind' :           AGENT_KIND_NONE ... AGENT_KIND_ADF_KIT,
65       'type' :           TYPE_BLACK ... AGENT_TYPE_UNSPECIFIED,      # aka color
66       'health' :         AGENT_HEALTH_OK ... AGENT_HEALTH_UNKNOWN,
67       'level' :          0 ... 100,
68       'level-trigger' :  AGENT_LEVEL_TRIGGER_SUFFICIENT_0 ... AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT,
69     }
70 """
71
72
73
74 # 'revision'
75 STATUS_REV_00 = 0x00
76 STATUS_REV_01 = 0x01
77 STATUS_REV_02 = 0x02
78 STATUS_REV_03 = 0x03
79 STATUS_REV_04 = 0x04
80 STATUS_REV_V  = 0xff
81 STATUS_REV_UNKNOWN = 0xfe
82
83 vstatus_xlate  = {'busy' : STATUS_PRINTER_BUSY,
84                    'idle' : STATUS_PRINTER_IDLE,
85                    'prnt' : STATUS_PRINTER_PRINTING,
86                    'offf' : STATUS_PRINTER_TURNING_OFF,
87                    'rprt' : STATUS_PRINTER_REPORT_PRINTING,
88                    'cncl' : STATUS_PRINTER_CANCELING,
89                    'iost' : STATUS_PRINTER_IO_STALL,
90                    'dryw' : STATUS_PRINTER_DRY_WAIT_TIME,
91                    'penc' : STATUS_PRINTER_PEN_CHANGE,
92                    'oopa' : STATUS_PRINTER_OUT_OF_PAPER,
93                    'bnej' : STATUS_PRINTER_BANNER_EJECT,
94                    'bnmz' : STATUS_PRINTER_BANNER_MISMATCH,
95                    'phmz' : STATUS_PRINTER_PHOTO_MISMATCH,
96                    'dpmz' : STATUS_PRINTER_DUPLEX_MISMATCH,
97                    'pajm' : STATUS_PRINTER_MEDIA_JAM,
98                    'cars' : STATUS_PRINTER_CARRIAGE_STALL,
99                    'paps' : STATUS_PRINTER_PAPER_STALL,
100                    'penf' : STATUS_PRINTER_PEN_FAILURE,
101                    'erro' : STATUS_PRINTER_HARD_ERROR,
102                    'pwdn' : STATUS_PRINTER_POWER_DOWN,
103                    'fpts' : STATUS_PRINTER_FRONT_PANEL_TEST,
104                    'clno' : STATUS_PRINTER_CLEAN_OUT_TRAY_MISSING}
105
106 REVISION_2_TYPE_MAP = {0 : AGENT_TYPE_NONE,
107                         1 : AGENT_TYPE_BLACK,
108                         2 : AGENT_TYPE_CYAN,
109                         3 : AGENT_TYPE_MAGENTA,
110                         4 : AGENT_TYPE_YELLOW,
111                         5 : AGENT_TYPE_BLACK,
112                         6 : AGENT_TYPE_CYAN,
113                         7 : AGENT_TYPE_MAGENTA,
114                         8 : AGENT_TYPE_YELLOW,
115                        }
116
117 STATUS_BLOCK_UNKNOWN = {'revision' : STATUS_REV_UNKNOWN,
118                          'agents' : [],
119                          'status-code' : STATUS_UNKNOWN,
120                        }
121
122 NUM_PEN_POS = {STATUS_REV_00 : 16,
123                 STATUS_REV_01 : 16,
124                 STATUS_REV_02 : 16,
125                 STATUS_REV_03 : 18,
126                 STATUS_REV_04 : 22}
127
128 PEN_DATA_SIZE = {STATUS_REV_00 : 8,
129                   STATUS_REV_01 : 8,
130                   STATUS_REV_02 : 4,
131                   STATUS_REV_03 : 8,
132                   STATUS_REV_04 : 8}
133
134 STATUS_POS = {STATUS_REV_00 : 14,
135                STATUS_REV_01 : 14,
136                STATUS_REV_02 : 14,
137                STATUS_REV_03 : 16,
138                STATUS_REV_04 : 20}
139
140 def parseSStatus(s, z=''):
141     revision = ''
142     pens = []
143     top_door = TOP_DOOR_NOT_PRESENT
144     stat = STATUS_UNKNOWN
145     supply_door = SUPPLY_DOOR_NOT_PRESENT
146     duplexer = DUPLEXER_NOT_PRESENT
147     photo_tray = PHOTO_TRAY_NOT_PRESENT
148     in_tray1 = IN_TRAY_NOT_PRESENT
149     in_tray2 = IN_TRAY_NOT_PRESENT
150     media_path = MEDIA_PATH_NOT_PRESENT
151     Z_SIZE = 6
152
153     try:
154         z1 = []
155         if len(z) > 0:
156             z_fields = z.split(',')
157
158             for z_field in z_fields:
159
160                 if len(z_field) > 2 and z_field[:2] == '05':
161                     z1s = z_field[2:]
162                     z1 = [int(x, 16) for x in z1s]
163
164         s1 = [int(x, 16) for x in s]
165
166         revision = s1[1]
167
168         assert STATUS_REV_00 <= revision <= STATUS_REV_04
169
170         top_door = bool(s1[2] & 0x8L) + s1[2] & 0x1L
171         supply_door = bool(s1[3] & 0x8L) + s1[3] & 0x1L
172         duplexer = bool(s1[4] & 0xcL) +  s1[4] & 0x1L
173         photo_tray = bool(s1[5] & 0x8L) + s1[5] & 0x1L
174
175         if revision == STATUS_REV_02:
176             in_tray1 = bool(s1[6] & 0x8L) + s1[6] & 0x1L
177             in_tray2 = bool(s1[7] & 0x8L) + s1[7] & 0x1L
178         else:
179             in_tray1 = bool(s1[6] & 0x8L)
180             in_tray2 = bool(s1[7] & 0x8L)
181
182         media_path = bool(s1[8] & 0x8L) + (s1[8] & 0x1L) + ((bool(s1[18] & 0x2L))<<1)
183         status_pos = STATUS_POS[revision]
184         status_byte = s1[status_pos]<<4
185         if status_byte != 48:
186             status_byte = (s1[status_pos]<<4) + s1[status_pos + 1]
187         stat = status_byte + STATUS_PRINTER_BASE
188
189         pen, c, d = {}, NUM_PEN_POS[revision]+1, 0
190         num_pens = s1[NUM_PEN_POS[revision]]
191         index = 0
192         pen_data_size = PEN_DATA_SIZE[revision]
193
194         log.debug("num_pens = %d" % num_pens)
195         for p in range(num_pens):
196             info = long(s[c : c + pen_data_size], 16)
197
198             pen['index'] = index
199
200             if pen_data_size == 4:
201                 pen['type'] = REVISION_2_TYPE_MAP.get(int((info & 0xf000L) >> 12L), 0)
202
203                 if index < (num_pens / 2):
204                     pen['kind'] = AGENT_KIND_HEAD
205                 else:
206                     pen['kind'] = AGENT_KIND_SUPPLY
207
208                 pen['level-trigger'] = int ((info & 0x0e00L) >> 9L)
209                 pen['health'] = int((info & 0x0180L) >> 7L)
210                 pen['level'] = int(info & 0x007fL)
211                 pen['id'] = 0x1f
212
213             elif pen_data_size == 8:
214                 pen['kind'] = bool(info & 0x80000000L) + ((bool(info & 0x40000000L))<<1L)
215                 pen['type'] = int((info & 0x3f000000L) >> 24L)
216                 pen['id'] = int((info & 0xf80000) >> 19L)
217                 pen['level-trigger'] = int((info & 0x70000L) >> 16L)
218                 pen['health'] = int((info & 0xc000L) >> 14L)
219                 pen['level'] = int(info & 0xffL)
220
221             else:
222                 log.error("Pen data size error")
223
224             if len(z1) > 0:
225                 # TODO: Determine cause of IndexError for C6100 (defect #1111)
226                 try:
227                     pen['dvc'] = long(z1s[d+1:d+5], 16)
228                     pen['virgin'] = bool(z1[d+5] & 0x8L)
229                     pen['hp-ink'] = bool(z1[d+5] & 0x4L)
230                     pen['known'] = bool(z1[d+5] & 0x2L)
231                     pen['ack'] = bool(z1[d+5] & 0x1L)
232                 except IndexError:
233                     pen['dvc'] = 0
234                     pen['virgin'] = 0
235                     pen['hp-ink'] = 0
236                     pen['known'] = 0
237                     pen['ack'] = 0
238
239             log.debug("pen %d %s" % (index, pen))
240
241             index += 1
242             pens.append(pen)
243             pen = {}
244             c += pen_data_size
245             d += Z_SIZE
246
247     except (IndexError, ValueError, TypeError), e:
248         log.warn("Status parsing error: %s" % str(e))
249
250     return {'revision' :    revision,
251              'agents' :      pens,
252              'top-door' :    top_door,
253              'status-code' : stat,
254              'supply-door' : supply_door,
255              'duplexer' :    duplexer,
256              'photo-tray' :  photo_tray,
257              'in-tray1' :    in_tray1,
258              'in-tray2' :    in_tray2,
259              'media-path' :  media_path,
260            }
261
262
263
264 # $HB0$NC0,ff,DN,IDLE,CUT,K0,C0,DP,NR,KP092,CP041
265 #     0    1  2  3    4   5  6  7  8  9     10
266 def parseVStatus(s):
267     pens, pen, c = [], {}, 0
268     fields = s.split(',')
269     log.debug(fields)
270     f0 = fields[0]
271
272     if len(f0) == 20:
273         # TODO: $H00000000$M00000000 style (OJ Pro 1150/70)
274         # Need spec
275         pass
276     elif len(f0) == 8:
277         for p in f0:
278             if c == 0:
279                 #assert p == '$'
280                 c += 1
281             elif c == 1:
282                 if p in ('a', 'A'):
283                     pen['type'], pen['kind'] = AGENT_TYPE_NONE, AGENT_KIND_NONE
284                 c += 1
285             elif c == 2:
286                 pen['health'] = AGENT_HEALTH_OK
287                 pen['kind'] = AGENT_KIND_HEAD_AND_SUPPLY
288                 if   p in ('b', 'B'): pen['type'] = AGENT_TYPE_BLACK
289                 elif p in ('c', 'C'): pen['type'] = AGENT_TYPE_CMY
290                 elif p in ('d', 'D'): pen['type'] = AGENT_TYPE_KCM
291                 elif p in ('u', 'U'): pen['type'], pen['health'] = AGENT_TYPE_NONE, AGENT_HEALTH_MISINSTALLED
292                 c += 1
293             elif c == 3:
294                 if p == '0': pen['state'] = 1
295                 else: pen['state'] = 0
296
297                 pen['level'] = 0
298                 i = 8
299
300                 while True:
301                     try:
302                         f = fields[i]
303                     except IndexError:
304                         break
305                     else:
306                         if f[:2] == 'KP' and pen['type'] == AGENT_TYPE_BLACK:
307                             pen['level'] = int(f[2:])
308                         elif f[:2] == 'CP' and pen['type'] == AGENT_TYPE_CMY:
309                             pen['level'] = int(f[2:])
310                     i += 1
311
312                 pens.append(pen)
313                 pen = {}
314                 c = 0
315     else:
316         pass
317
318     try:
319         fields[2]
320     except IndexError:
321         top_lid = 1 # something went wrong!
322     else:
323         if fields[2] == 'DN':
324             top_lid = 1
325         else:
326             top_lid = 2
327
328     try:
329         stat = vstatus_xlate.get(fields[3].lower(), STATUS_PRINTER_IDLE)
330     except IndexError:
331         stat = STATUS_PRINTER_IDLE # something went wrong!
332
333     return {'revision' :   STATUS_REV_V,
334              'agents' :     pens,
335              'top-door' :   top_lid,
336              'status-code': stat,
337              'supply-door': SUPPLY_DOOR_NOT_PRESENT,
338              'duplexer' :   DUPLEXER_NOT_PRESENT,
339              'photo-tray' : PHOTO_TRAY_NOT_PRESENT,
340              'in-tray1' :   IN_TRAY_NOT_PRESENT,
341              'in-tray2' :   IN_TRAY_NOT_PRESENT,
342              'media-path' : MEDIA_PATH_CUT_SHEET, # ?
343            }
344
345
346 def parseStatus(DeviceID):
347     if 'VSTATUS' in DeviceID:
348          return parseVStatus(DeviceID['VSTATUS'])
349     elif 'S' in DeviceID:
350         return parseSStatus(DeviceID['S'], DeviceID.get('Z', ''))
351     else:
352         return STATUS_BLOCK_UNKNOWN
353
354 def LaserJetDeviceStatusToPrinterStatus(device_status, printer_status, detected_error_state):
355     stat = STATUS_PRINTER_IDLE
356
357     if device_status in (pml.DEVICE_STATUS_WARNING, pml.DEVICE_STATUS_DOWN):
358
359         if detected_error_state & pml.DETECTED_ERROR_STATE_LOW_PAPER_MASK and \
360             not (detected_error_state & pml.DETECTED_ERROR_STATE_NO_PAPER_MASK):
361             stat = STATUS_PRINTER_LOW_PAPER
362
363         elif detected_error_state & pml.DETECTED_ERROR_STATE_NO_PAPER_MASK:
364             stat = STATUS_PRINTER_OUT_OF_PAPER
365
366         elif detected_error_state & pml.DETECTED_ERROR_STATE_DOOR_OPEN_MASK:
367             stat = STATUS_PRINTER_DOOR_OPEN
368
369         elif detected_error_state & pml.DETECTED_ERROR_STATE_JAMMED_MASK:
370             stat = STATUS_PRINTER_MEDIA_JAM
371
372         elif detected_error_state & pml.DETECTED_ERROR_STATE_OUT_CART_MASK:
373             stat = STATUS_PRINTER_NO_TONER
374
375         elif detected_error_state & pml.DETECTED_ERROR_STATE_LOW_CART_MASK:
376             stat = STATUS_PRINTER_LOW_TONER
377
378         elif detected_error_state == pml.DETECTED_ERROR_STATE_SERVICE_REQUEST_MASK:
379             stat = STATUS_PRINTER_SERVICE_REQUEST
380
381         elif detected_error_state & pml.DETECTED_ERROR_STATE_OFFLINE_MASK:
382             stat = STATUS_PRINTER_OFFLINE
383
384     else:
385
386         if printer_status == pml.PRINTER_STATUS_IDLE:
387             stat = STATUS_PRINTER_IDLE
388
389         elif printer_status == pml.PRINTER_STATUS_PRINTING:
390             stat = STATUS_PRINTER_PRINTING
391
392         elif printer_status == pml.PRINTER_STATUS_WARMUP:
393             stat = STATUS_PRINTER_WARMING_UP
394
395     return stat
396
397 # Map from ISO 10175/10180 to HPLIP types
398 COLORANT_INDEX_TO_AGENT_TYPE_MAP = {
399                                     'other' :   AGENT_TYPE_UNSPECIFIED,
400                                     'unknown' : AGENT_TYPE_UNSPECIFIED,
401                                     'blue' :    AGENT_TYPE_BLUE,
402                                     'cyan' :    AGENT_TYPE_CYAN,
403                                     'magenta':  AGENT_TYPE_MAGENTA,
404                                     'yellow' :  AGENT_TYPE_YELLOW,
405                                     'black' :   AGENT_TYPE_BLACK,
406                                    }
407
408 MARKER_SUPPLES_TYPE_TO_AGENT_KIND_MAP = {
409     pml.OID_MARKER_SUPPLIES_TYPE_OTHER :              AGENT_KIND_UNKNOWN,
410     pml.OID_MARKER_SUPPLIES_TYPE_UNKNOWN :            AGENT_KIND_UNKNOWN,
411     pml.OID_MARKER_SUPPLIES_TYPE_TONER :              AGENT_KIND_TONER_CARTRIDGE,
412     pml.OID_MARKER_SUPPLIES_TYPE_WASTE_TONER :        AGENT_KIND_UNKNOWN,
413     pml.OID_MARKER_SUPPLIES_TYPE_INK :                AGENT_KIND_SUPPLY,
414     pml.OID_MARKER_SUPPLIES_TYPE_INK_CART :           AGENT_KIND_HEAD_AND_SUPPLY,
415     pml.OID_MARKER_SUPPLIES_TYPE_INK_RIBBON :         AGENT_KIND_HEAD_AND_SUPPLY,
416     pml.OID_MARKER_SUPPLIES_TYPE_WASTE_INK :          AGENT_KIND_UNKNOWN,
417     pml.OID_MARKER_SUPPLIES_TYPE_OPC :                AGENT_KIND_DRUM_KIT,
418     pml.OID_MARKER_SUPPLIES_TYPE_DEVELOPER :          AGENT_KIND_UNKNOWN,
419     pml.OID_MARKER_SUPPLIES_TYPE_FUSER_OIL :          AGENT_KIND_UNKNOWN,
420     pml.OID_MARKER_SUPPLIES_TYPE_SOLID_WAX :          AGENT_KIND_UNKNOWN,
421     pml.OID_MARKER_SUPPLIES_TYPE_RIBBON_WAX :         AGENT_KIND_UNKNOWN,
422     pml.OID_MARKER_SUPPLIES_TYPE_WASTE_WAX :          AGENT_KIND_UNKNOWN,
423     pml.OID_MARKER_SUPPLIES_TYPE_FUSER :              AGENT_KIND_MAINT_KIT,
424     pml.OID_MARKER_SUPPLIES_TYPE_CORONA_WIRE :        AGENT_KIND_UNKNOWN,
425     pml.OID_MARKER_SUPPLIES_TYPE_FUSER_OIL_WICK :     AGENT_KIND_UNKNOWN,
426     pml.OID_MARKER_SUPPLIES_TYPE_CLEANER_UNIT :       AGENT_KIND_UNKNOWN,
427     pml.OID_MARKER_SUPPLIES_TYPE_FUSER_CLEANING_PAD : AGENT_KIND_UNKNOWN,
428     pml.OID_MARKER_SUPPLIES_TYPE_TRANSFER_UNIT :      AGENT_KIND_TRANSFER_KIT,
429     pml.OID_MARKER_SUPPLIES_TYPE_TONER_CART :         AGENT_KIND_TONER_CARTRIDGE,
430     pml.OID_MARKER_SUPPLIES_TYPE_FUSER_OILER :        AGENT_KIND_UNKNOWN,
431     pml.OID_MARKER_SUPPLIES_TYPE_ADF_MAINT_KIT :      AGENT_KIND_ADF_KIT,
432 }
433
434
435 def StatusType3( dev, parsedID ): # LaserJet Status (PML/SNMP)
436     try:
437         dev.openPML()
438         #result_code, on_off_line = dev.getPML( pml.OID_ON_OFF_LINE, pml.INT_SIZE_BYTE )
439         #result_code, sleep_mode = dev.getPML( pml.OID_SLEEP_MODE, pml.INT_SIZE_BYTE )
440         result_code, printer_status = dev.getPML( pml.OID_PRINTER_STATUS, pml.INT_SIZE_BYTE )
441         result_code, device_status = dev.getPML( pml.OID_DEVICE_STATUS, pml.INT_SIZE_BYTE )
442         result_code, cover_status = dev.getPML( pml.OID_COVER_STATUS, pml.INT_SIZE_BYTE )
443         result_code, value = dev.getPML( pml.OID_DETECTED_ERROR_STATE )
444     except Error:
445        dev.closePML()
446
447        return {'revision' :    STATUS_REV_UNKNOWN,
448                  'agents' :      [],
449                  'top-door' :    0,
450                  'status-code' : STATUS_UNKNOWN,
451                  'supply-door' : 0,
452                  'duplexer' :    1,
453                  'photo-tray' :  0,
454                  'in-tray1' :    0,
455                  'in-tray2' :    0,
456                  'media-path' :  0,
457                }
458
459     try:
460         detected_error_state = struct.unpack( 'B', value[0])[0]
461     except (IndexError, TypeError):
462         detected_error_state = pml.DETECTED_ERROR_STATE_OFFLINE_MASK
463
464     agents, x = [], 1
465
466     while True:
467         log.debug( "%s Agent: %d %s" % ("*"*10, x, "*"*10))
468         log.debug("OID_MARKER_SUPPLIES_TYPE_%d:" % x)
469         oid = ( pml.OID_MARKER_SUPPLIES_TYPE_x % x, pml.OID_MARKER_SUPPLIES_TYPE_x_TYPE )
470         result_code, value = dev.getPML( oid, pml.INT_SIZE_BYTE )
471
472         if result_code != ERROR_SUCCESS or value is None:
473             log.debug("End of supply information.")
474             break
475
476         for a in MARKER_SUPPLES_TYPE_TO_AGENT_KIND_MAP:
477             if value == a:
478                 agent_kind = MARKER_SUPPLES_TYPE_TO_AGENT_KIND_MAP[a]
479                 break
480         else:
481             agent_kind = AGENT_KIND_UNKNOWN
482
483         # TODO: Deal with printers that return -1 and -2 for level and max (LJ3380)
484
485         log.debug("OID_MARKER_SUPPLIES_LEVEL_%d:" % x)
486         oid = ( pml.OID_MARKER_SUPPLIES_LEVEL_x % x, pml.OID_MARKER_SUPPLIES_LEVEL_x_TYPE )
487         result_code, agent_level = dev.getPML( oid )
488
489         if result_code != ERROR_SUCCESS:
490             log.debug("Failed")
491             break
492
493         log.debug( 'agent%d-level: %d' % ( x, agent_level ) )
494         log.debug("OID_MARKER_SUPPLIES_MAX_%d:" % x)
495         oid = ( pml.OID_MARKER_SUPPLIES_MAX_x % x, pml.OID_MARKER_SUPPLIES_MAX_x_TYPE )
496         result_code, agent_max = dev.getPML( oid )
497
498         if agent_max == 0: agent_max = 1
499
500         if result_code != ERROR_SUCCESS:
501             log.debug("Failed")
502             break
503
504         log.debug( 'agent%d-max: %d' % ( x, agent_max ) )
505         log.debug("OID_MARKER_SUPPLIES_COLORANT_INDEX_%d:" % x)
506         oid = ( pml.OID_MARKER_SUPPLIES_COLORANT_INDEX_x % x, pml.OID_MARKER_SUPPLIES_COLORANT_INDEX_x_TYPE )
507         result_code, colorant_index = dev.getPML( oid )
508
509         if result_code != ERROR_SUCCESS: # 3080, 3055 will fail here
510             log.debug("Failed")
511             agent_type = AGENT_TYPE_BLACK
512             #break
513         else:
514             log.debug("Colorant index: %d" % colorant_index)
515
516             log.debug("OID_MARKER_COLORANT_VALUE_%d" % x)
517             oid = ( pml.OID_MARKER_COLORANT_VALUE_x % colorant_index, pml.OID_MARKER_COLORANT_VALUE_x_TYPE )
518             result_code, colorant_value = dev.getPML( oid )
519
520             if result_code != ERROR_SUCCESS:
521                 log.debug("Failed. Defaulting to black.")
522                 agent_type = AGENT_TYPE_BLACK
523             #else:
524             if 1:
525                 if agent_kind in (AGENT_KIND_MAINT_KIT, AGENT_KIND_ADF_KIT,
526                                   AGENT_KIND_DRUM_KIT, AGENT_KIND_TRANSFER_KIT):
527
528                     agent_type = AGENT_TYPE_UNSPECIFIED
529
530                 else:
531                     agent_type = AGENT_TYPE_BLACK
532
533                     if result_code != ERROR_SUCCESS:
534                         log.debug("OID_MARKER_SUPPLIES_DESCRIPTION_%d:" % x)
535                         oid = (pml.OID_MARKER_SUPPLIES_DESCRIPTION_x % x, pml.OID_MARKER_SUPPLIES_DESCRIPTION_x_TYPE)
536                         result_code, colorant_value = dev.getPML( oid )
537
538                         if result_code != ERROR_SUCCESS:
539                             log.debug("Failed")
540                             break
541
542                         if colorant_value is not None:
543                             log.debug("colorant value: %s" % colorant_value)
544                             colorant_value = colorant_value.lower().strip()
545
546                             for c in COLORANT_INDEX_TO_AGENT_TYPE_MAP:
547                                 if colorant_value.find(c) >= 0:
548                                     agent_type = COLORANT_INDEX_TO_AGENT_TYPE_MAP[c]
549                                     break
550                             else:
551                                 agent_type = AGENT_TYPE_BLACK
552
553                     else: # SUCCESS
554                         if colorant_value is not None:
555                             log.debug("colorant value: %s" % colorant_value)
556                             agent_type = COLORANT_INDEX_TO_AGENT_TYPE_MAP.get( colorant_value, AGENT_TYPE_BLACK )
557
558                         if agent_type == AGENT_TYPE_NONE:
559                             if agent_kind == AGENT_KIND_TONER_CARTRIDGE:
560                                 agent_type = AGENT_TYPE_BLACK
561                             else:
562                                 agent_type = AGENT_TYPE_UNSPECIFIED
563
564         log.debug("OID_MARKER_STATUS_%d:" % x)
565         oid = ( pml.OID_MARKER_STATUS_x % x, pml.OID_MARKER_STATUS_x_TYPE )
566         result_code, agent_status = dev.getPML( oid )
567
568         if result_code != ERROR_SUCCESS:
569             log.debug("Failed")
570             agent_trigger = AGENT_LEVEL_TRIGGER_SUFFICIENT_0
571             agent_health = AGENT_HEALTH_OK
572         else:
573             agent_trigger = AGENT_LEVEL_TRIGGER_SUFFICIENT_0
574
575             if agent_status is None:
576                 agent_health = AGENT_HEALTH_OK
577
578             elif agent_status == pml.OID_MARKER_STATUS_OK:
579                 agent_health = AGENT_HEALTH_OK
580
581             elif agent_status == pml.OID_MARKER_STATUS_MISINSTALLED:
582                 agent_health = AGENT_HEALTH_MISINSTALLED
583
584             elif agent_status in ( pml.OID_MARKER_STATUS_LOW_TONER_CONT,
585                                    pml.OID_MARKER_STATUS_LOW_TONER_STOP ):
586
587                 agent_health = AGENT_HEALTH_OK
588                 agent_trigger = AGENT_LEVEL_TRIGGER_MAY_BE_LOW
589
590             else:
591                 agent_health = AGENT_HEALTH_OK
592
593         agent_level = int(agent_level/agent_max * 100)
594
595         log.debug("agent%d: kind=%d, type=%d, health=%d, level=%d, level-trigger=%d" % \
596             (x, agent_kind, agent_type, agent_health, agent_level, agent_trigger))
597
598
599         agents.append({'kind' : agent_kind,
600                        'type' : agent_type,
601                        'health' : agent_health,
602                        'level' : agent_level,
603                        'level-trigger' : agent_trigger,})
604
605         x += 1
606
607         if x > 20:
608             break
609
610
611     printer_status = printer_status or STATUS_PRINTER_IDLE
612     log.debug("printer_status=%d" % printer_status)
613     device_status = device_status or pml.DEVICE_STATUS_RUNNING
614     log.debug("device_status=%d" % device_status)
615     cover_status = cover_status or pml.COVER_STATUS_CLOSED
616     log.debug("cover_status=%d" % cover_status)
617     detected_error_state = detected_error_state or pml.DETECTED_ERROR_STATE_NO_ERROR
618     log.debug("detected_error_state=%d (0x%x)" % (detected_error_state, detected_error_state))
619
620     stat = LaserJetDeviceStatusToPrinterStatus(device_status, printer_status, detected_error_state)
621
622     log.debug("Printer status=%d" % stat)
623
624     if stat == STATUS_PRINTER_DOOR_OPEN:
625         supply_door = 0
626     else:
627         supply_door = 1
628
629     return {'revision' :    STATUS_REV_UNKNOWN,
630              'agents' :      agents,
631              'top-door' :    cover_status,
632              'status-code' : stat,
633              'supply-door' : supply_door,
634              'duplexer' :    1,
635              'photo-tray' :  0,
636              'in-tray1' :    1,
637              'in-tray2' :    1,
638              'media-path' :  1,
639            }
640
641 def setup_panel_translator():
642     printables = list(
643 """0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
644 !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~""")
645
646     map = {}
647     for x in [chr(x) for x in range(0,256)]:
648         if x in printables:
649             map[x] = x
650         else:
651             map[x] = '\x20'
652
653     map.update({'\x10' : '\xab',
654                     '\x11' : '\xbb',
655                     '\x12' : '\xa3',
656                     '\x13' : '\xbb',
657                     '\x80' : '\xab',
658                     '\x81' : '\xbb',
659                     '\x82' : '\x2a',
660                     '\x83' : '\x2a',
661                     '\x85' : '\x2a',
662                     '\xa0' : '\xab',
663                     '\x1f' : '\x3f',
664                     '='    : '\x20',
665                 })
666
667     frm, to = '', ''
668     map_keys = map.keys()
669     map_keys.sort()
670     for x in map_keys:
671         frm = ''.join([frm, x])
672         to = ''.join([to, map[x]])
673
674     global PANEL_TRANSLATOR_FUNC
675     PANEL_TRANSLATOR_FUNC = utils.Translator(frm, to)
676
677 PANEL_TRANSLATOR_FUNC = None
678 setup_panel_translator()
679
680
681 def PanelCheck(dev):
682     line1, line2 = '', ''
683
684     if dev.io_mode not in (IO_MODE_RAW, IO_MODE_UNI):
685
686         try:
687             dev.openPML()
688         except Error:
689             pass
690         else:
691
692             oids = [(pml.OID_HP_LINE1, pml.OID_HP_LINE2),
693                      (pml.OID_SPM_LINE1, pml.OID_SPM_LINE2)]
694
695             for oid1, oid2 in oids:
696                 result, line1 = dev.getPML(oid1)
697
698                 if result < pml.ERROR_MAX_OK:
699                     line1 = PANEL_TRANSLATOR_FUNC(line1).rstrip()
700
701                     if '\x0a' in line1:
702                         line1, line2 = line1.split('\x0a', 1)
703                         break
704
705                     result, line2 = dev.getPML(oid2)
706
707                     if result < pml.ERROR_MAX_OK:
708                         line2 = PANEL_TRANSLATOR_FUNC(line2).rstrip()
709                         break
710
711     return bool(line1 or line2), line1 or '', line2 or ''
712
713
714 BATTERY_HEALTH_MAP = {0 : AGENT_HEALTH_OK,
715                        1 : AGENT_HEALTH_OVERTEMP,
716                        2 : AGENT_HEALTH_CHARGING,
717                        3 : AGENT_HEALTH_MISINSTALLED,
718                        4 : AGENT_HEALTH_FAILED,
719                       }
720
721
722 BATTERY_TRIGGER_MAP = {0 : AGENT_LEVEL_TRIGGER_SUFFICIENT_0,
723                         1 : AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT,
724                         2 : AGENT_LEVEL_TRIGGER_PROBABLY_OUT,
725                         3 : AGENT_LEVEL_TRIGGER_SUFFICIENT_4,
726                         4 : AGENT_LEVEL_TRIGGER_SUFFICIENT_2,
727                         5 : AGENT_LEVEL_TRIGGER_SUFFICIENT_0,
728                        }
729
730 BATTERY_PML_TRIGGER_MAP = {
731         (100, 80)  : AGENT_LEVEL_TRIGGER_SUFFICIENT_0,
732         (79,  60)  : AGENT_LEVEL_TRIGGER_SUFFICIENT_1,
733         (59,  40)  : AGENT_LEVEL_TRIGGER_SUFFICIENT_2,
734         (39,  30)  : AGENT_LEVEL_TRIGGER_SUFFICIENT_3,
735         (29,  20)  : AGENT_LEVEL_TRIGGER_SUFFICIENT_4,
736         (19,  10)  : AGENT_LEVEL_TRIGGER_MAY_BE_LOW,
737         (9,    5)  : AGENT_LEVEL_TRIGGER_PROBABLY_OUT,
738         (4,   -1)  : AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT,
739         }
740
741
742 def BatteryCheck(dev, status_block, battery_check):
743     try_dynamic_counters = False
744
745     try:
746         try:
747             dev.openPML()
748         except Error:
749             if battery_check == STATUS_BATTERY_CHECK_STD:
750                 log.debug("PML channel open failed. Trying dynamic counters...")
751                 try_dynamic_counters = True
752         else:
753             if battery_check == STATUS_BATTERY_CHECK_PML:
754                 result, battery_level = dev.getPML(pml.OID_BATTERY_LEVEL_2)
755
756                 if result > pml.ERROR_MAX_OK:
757                     status_block['agents'].append({
758                         'kind'   : AGENT_KIND_INT_BATTERY,
759                         'type'   : AGENT_TYPE_UNSPECIFIED,
760                         'health' : AGENT_HEALTH_UNKNOWN,
761                         'level'  : 0,
762                         'level-trigger' : AGENT_LEVEL_TRIGGER_SUFFICIENT_0,
763                         })
764                     return
765
766                 else:
767                     status_block['agents'].append({
768                         'kind'   : AGENT_KIND_INT_BATTERY,
769                         'type'   : AGENT_TYPE_UNSPECIFIED,
770                         'health' : AGENT_HEALTH_OK,
771                         'level'  : battery_level,
772                         'level-trigger' : AGENT_LEVEL_TRIGGER_SUFFICIENT_0,
773                         })
774                     return
775
776             else: # STATUS_BATTERY_CHECK_STD
777                 result, battery_level = dev.getPML(pml.OID_BATTERY_LEVEL)
778                 result, power_mode =  dev.getPML(pml.OID_POWER_MODE)
779
780                 if battery_level is not None and \
781                     power_mode is not None:
782
783                     if power_mode & pml.POWER_MODE_BATTERY_LEVEL_KNOWN and \
784                         battery_level >= 0:
785
786                         for x in BATTERY_PML_TRIGGER_MAP:
787                             if x[0] >= battery_level > x[1]:
788                                 battery_trigger_level = BATTERY_PML_TRIGGER_MAP[x]
789                                 break
790
791                         if power_mode & pml.POWER_MODE_CHARGING:
792                             agent_health = AGENT_HEALTH_CHARGING
793
794                         elif power_mode & pml.POWER_MODE_DISCHARGING:
795                             agent_health = AGENT_HEALTH_DISCHARGING
796
797                         else:
798                             agent_health = AGENT_HEALTH_OK
799
800                         status_block['agents'].append({
801                             'kind'   : AGENT_KIND_INT_BATTERY,
802                             'type'   : AGENT_TYPE_UNSPECIFIED,
803                             'health' : agent_health,
804                             'level'  : battery_level,
805                             'level-trigger' : battery_trigger_level,
806                             })
807                         return
808
809                     else:
810                         status_block['agents'].append({
811                             'kind'   : AGENT_KIND_INT_BATTERY,
812                             'type'   : AGENT_TYPE_UNSPECIFIED,
813                             'health' : AGENT_HEALTH_UNKNOWN,
814                             'level'  : 0,
815                             'level-trigger' : AGENT_LEVEL_TRIGGER_SUFFICIENT_0,
816                             })
817                         return
818
819                 else:
820                     try_dynamic_counters = True
821
822     finally:
823         dev.closePML()
824
825
826     if battery_check == STATUS_BATTERY_CHECK_STD and \
827         try_dynamic_counters:
828
829         try:
830             try:
831                 battery_health = dev.getDynamicCounter(200)
832                 battery_trigger_level = dev.getDynamicCounter(201)
833                 battery_level = dev.getDynamicCounter(202)
834
835                 status_block['agents'].append({
836                     'kind'   : AGENT_KIND_INT_BATTERY,
837                     'type'   : AGENT_TYPE_UNSPECIFIED,
838                     'health' : BATTERY_HEALTH_MAP[battery_health],
839                     'level'  : battery_level,
840                     'level-trigger' : BATTERY_TRIGGER_MAP[battery_trigger_level],
841                     })
842             except Error:
843                 status_block['agents'].append({
844                     'kind'   : AGENT_KIND_INT_BATTERY,
845                     'type'   : AGENT_TYPE_UNSPECIFIED,
846                     'health' : AGENT_HEALTH_UNKNOWN,
847                     'level'  : 0,
848                     'level-trigger' : AGENT_LEVEL_TRIGGER_SUFFICIENT_0,
849                     })
850         finally:
851             dev.closePrint()
852
853     else:
854         status_block['agents'].append({
855             'kind'   : AGENT_KIND_INT_BATTERY,
856             'type'   : AGENT_TYPE_UNSPECIFIED,
857             'health' : AGENT_HEALTH_UNKNOWN,
858             'level'  : 0,
859             'level-trigger' : AGENT_LEVEL_TRIGGER_SUFFICIENT_0,
860             })
861
862
863
864 # this works for 2 pen products that allow 1 or 2 pens inserted
865 # from: k, kcm, cmy, ggk
866 def getPenConfiguration(s): # s=status dict from parsed device ID
867     pens = [p['type'] for p in s['agents']]
868
869     if utils.all(pens, lambda x : x==AGENT_TYPE_NONE):
870         return AGENT_CONFIG_NONE
871
872     if AGENT_TYPE_NONE in pens:
873
874         if AGENT_TYPE_BLACK in pens:
875             return AGENT_CONFIG_BLACK_ONLY
876
877         elif AGENT_TYPE_CMY in pens:
878             return AGENT_CONFIG_COLOR_ONLY
879
880         elif AGENT_TYPE_KCM in pens:
881             return AGENT_CONFIG_PHOTO_ONLY
882
883         elif AGENT_TYPE_GGK in pens:
884             return AGENT_CONFIG_GREY_ONLY
885
886         else:
887             return AGENT_CONFIG_INVALID
888
889     else:
890         if AGENT_TYPE_BLACK in pens and AGENT_TYPE_CMY in pens:
891             return AGENT_CONFIG_COLOR_AND_BLACK
892
893         elif AGENT_TYPE_CMY in pens and AGENT_TYPE_KCM in pens:
894             return AGENT_CONFIG_COLOR_AND_PHOTO
895
896         elif AGENT_TYPE_CMY in pens and AGENT_TYPE_GGK in pens:
897             return AGENT_CONFIG_COLOR_AND_GREY
898
899         else:
900             return AGENT_CONFIG_INVALID
901
902
903 def getFaxStatus(dev):
904     tx_active, rx_active = False, False
905
906     if dev.io_mode not in (IO_MODE_UNI, IO_MODE_RAW):
907         try:
908             dev.openPML()
909
910             result_code, tx_state = dev.getPML(pml.OID_FAXJOB_TX_STATUS)
911
912             if result_code == ERROR_SUCCESS and tx_state:
913                 if tx_state not in (pml.FAXJOB_TX_STATUS_IDLE, pml.FAXJOB_TX_STATUS_DONE):
914                     tx_active = True
915
916             result_code, rx_state = dev.getPML(pml.OID_FAXJOB_RX_STATUS)
917
918             if result_code == ERROR_SUCCESS and rx_state:
919                 if rx_state not in (pml.FAXJOB_RX_STATUS_IDLE, pml.FAXJOB_RX_STATUS_DONE):
920                     rx_active = True
921
922         finally:
923             dev.closePML()
924
925     return tx_active, rx_active
926
927
928 TYPE6_STATUS_CODE_MAP = {
929      0    : STATUS_PRINTER_IDLE, #</DevStatusUnknown>
930     -19928: STATUS_PRINTER_IDLE,
931     -18995: STATUS_PRINTER_CANCELING,
932     -17974: STATUS_PRINTER_WARMING_UP,
933     -17973: STATUS_PRINTER_PEN_CLEANING, # sic
934     -18993: STATUS_PRINTER_BUSY,
935     -17949: STATUS_PRINTER_BUSY,
936     -19720: STATUS_PRINTER_MANUAL_DUPLEX_BLOCK,
937     -19678: STATUS_PRINTER_BUSY,
938     -19695: STATUS_PRINTER_OUT_OF_PAPER,
939     -17985: STATUS_PRINTER_MEDIA_JAM,
940     -19731: STATUS_PRINTER_OUT_OF_PAPER,
941     -18974: STATUS_PRINTER_BUSY, #?
942     -19730: STATUS_PRINTER_OUT_OF_PAPER,
943     -19729: STATUS_PRINTER_OUT_OF_PAPER,
944     -19933: STATUS_PRINTER_HARD_ERROR, # out of memory
945     -17984: STATUS_PRINTER_DOOR_OPEN,
946     -19694: STATUS_PRINTER_DOOR_OPEN,
947     -18992: STATUS_PRINTER_MANUAL_FEED_BLOCKED, # ?
948     -19690: STATUS_PRINTER_MEDIA_JAM, # tray 1
949     -19689: STATUS_PRINTER_MEDIA_JAM, # tray 2
950     -19611: STATUS_PRINTER_MEDIA_JAM, # tray 3
951     -19686: STATUS_PRINTER_MEDIA_JAM,
952     -19688: STATUS_PRINTER_MEDIA_JAM, # paper path
953     -19685: STATUS_PRINTER_MEDIA_JAM, # cart area
954     -19684: STATUS_PRINTER_MEDIA_JAM, # output bin
955     -18848: STATUS_PRINTER_MEDIA_JAM, # duplexer
956     -18847: STATUS_PRINTER_MEDIA_JAM, # door open
957     -18846: STATUS_PRINTER_MEDIA_JAM, # tray 2
958     -19687: STATUS_PRINTER_MEDIA_JAM, # open door
959     -17992: STATUS_PRINTER_MEDIA_JAM, # mispick
960     -19700: STATUS_PRINTER_HARD_ERROR, # invalid driver
961     -17996: STATUS_PRINTER_FUSER_ERROR, # fuser error
962     -17983: STATUS_PRINTER_FUSER_ERROR,
963     -17982: STATUS_PRINTER_FUSER_ERROR,
964     -17981: STATUS_PRINTER_FUSER_ERROR,
965     -17971: STATUS_PRINTER_FUSER_ERROR,
966     -17995: STATUS_PRINTER_HARD_ERROR, # beam error
967     -17994: STATUS_PRINTER_HARD_ERROR, # scanner error
968     -17993: STATUS_PRINTER_HARD_ERROR, # fan error
969     -18994: STATUS_PRINTER_HARD_ERROR,
970     -17986: STATUS_PRINTER_HARD_ERROR,
971     -19904: STATUS_PRINTER_HARD_ERROR,
972     -19701: STATUS_PRINTER_NON_HP_INK, # [sic]
973     -19613: STATUS_PRINTER_IDLE, # HP
974     -19654: STATUS_PRINTER_NON_HP_INK, # [sic]
975     -19682: STATUS_PRINTER_HARD_ERROR, # resinstall
976     -19693: STATUS_PRINTER_IDLE, # ?? To Accept
977     -19752: STATUS_PRINTER_LOW_TONER,
978     -19723: STATUS_PRINTER_BUSY,
979     -19703: STATUS_PRINTER_BUSY,
980     -19739: STATUS_PRINTER_NO_TONER,
981     -19927: STATUS_PRINTER_BUSY,
982     -19932: STATUS_PRINTER_BUSY,
983     -19931: STATUS_PRINTER_BUSY,
984     -11989: STATUS_PRINTER_BUSY,
985     -11995: STATUS_PRINTER_BUSY, # ADF loaded
986     -19954: STATUS_PRINTER_CANCELING,
987     -19955: STATUS_PRINTER_REPORT_PRINTING,
988     -19956: STATUS_PRINTER_REPORT_PRINTING,
989     -19934: STATUS_PRINTER_HARD_ERROR,
990     -19930: STATUS_PRINTER_BUSY,
991     -11990: STATUS_PRINTER_DOOR_OPEN,
992     -11999: STATUS_PRINTER_MEDIA_JAM, # ADF
993     -12000: STATUS_PRINTER_MEDIA_JAM, # ADF
994     -11998: STATUS_PRINTER_MEDIA_JAM, # ADF
995     -11986: STATUS_PRINTER_HARD_ERROR, # scanner
996     -11994: STATUS_PRINTER_BUSY,
997     -14967: STATUS_PRINTER_BUSY,
998     -19912: STATUS_PRINTER_HARD_ERROR,
999     -14962: STATUS_PRINTER_BUSY, # copy pending
1000     -14971: STATUS_PRINTER_BUSY, # copying
1001     -14973: STATUS_PRINTER_BUSY, # copying being canceled
1002     -14972: STATUS_PRINTER_BUSY, # copying canceled
1003     -14966: STATUS_PRINTER_DOOR_OPEN,
1004     -14974: STATUS_PRINTER_MEDIA_JAM,
1005     -14969: STATUS_PRINTER_HARD_ERROR,
1006     -14968: STATUS_PRINTER_HARD_ERROR,
1007     -12996: STATUS_PRINTER_BUSY, # scan
1008     -12994: STATUS_PRINTER_BUSY, # scan
1009     -12993: STATUS_PRINTER_BUSY, # scan
1010     -12991: STATUS_PRINTER_BUSY, # scan
1011     -12995: STATUS_PRINTER_BUSY, # scan
1012     -12997: STATUS_PRINTER_HARD_ERROR, # scan
1013     -12990: STATUS_PRINTER_BUSY,
1014     -12998: STATUS_PRINTER_BUSY,
1015     -13000: STATUS_PRINTER_DOOR_OPEN,
1016     -12999: STATUS_PRINTER_MEDIA_JAM,
1017     -13859: STATUS_PRINTER_BUSY,
1018     -13858: STATUS_PRINTER_BUSY, #</DevStatusDialingOut>
1019     -13868: STATUS_PRINTER_BUSY, #</DevStatusRedialPending>
1020     -13867: STATUS_PRINTER_BUSY, #</DevStatusFaxSendCanceled>
1021     -13857: STATUS_PRINTER_BUSY, #</DevStatusConnecting>
1022     -13856: STATUS_PRINTER_BUSY, #</DevStatusSendingPage>
1023     -13855: STATUS_PRINTER_BUSY, #</DevStatusOnePageSend>
1024     -13854: STATUS_PRINTER_BUSY, #</DevStatusMultiplePagesSent>
1025     -13853: STATUS_PRINTER_BUSY, #</DevStatusSenderCancelingFax>
1026     -13839: STATUS_PRINTER_BUSY, #</DevStatusIncomingCall>
1027     -13842: STATUS_PRINTER_BUSY, #</DevStatusBlockingFax>
1028     -13838: STATUS_PRINTER_BUSY, #</DevStatusReceivingFax>
1029     -13847: STATUS_PRINTER_BUSY, #</DevStatusSinglePageReceived>
1030     -13846: STATUS_PRINTER_BUSY, #</DevStatusDoublePagesReceived>
1031     -13845: STATUS_PRINTER_BUSY, #</DevStatusTriplePagesReceived>
1032     -13844: STATUS_PRINTER_BUSY, #</DevStatusPrintingFax>
1033     -13840: STATUS_PRINTER_BUSY, #</DevStatusCancelingFaxPrint>
1034     -13843: STATUS_PRINTER_BUSY, #</DevStatusFaxCancelingReceive>
1035     -13850: STATUS_PRINTER_BUSY, #</DevStatusFaxCanceledReceive>
1036     -13851: STATUS_PRINTER_BUSY, #</DevStatusFaxDelayedSendMemoryFull>
1037     -13836: STATUS_PRINTER_BUSY, #</DevStatusNoDialTone>
1038     -13864: STATUS_PRINTER_BUSY, #</DevStatusNoFaxAnswer>
1039     -13863: STATUS_PRINTER_BUSY, #</DevStatusFaxBusy>
1040     -13865: STATUS_PRINTER_BUSY, #</DevStatusNoDocumentSent>
1041     -13862: STATUS_PRINTER_BUSY, #</DevStatusFaxSendError>
1042     -13837: STATUS_PRINTER_BUSY, #</DevStatusT30Error>
1043     -13861: STATUS_PRINTER_BUSY, #</DevStatusFaxMemoryFullSend>
1044     -13866: STATUS_PRINTER_BUSY, #</DevStatusADFNotCleared>
1045     -13841: STATUS_PRINTER_BUSY, #</DevStatusNoFaxDetected>
1046     -13848: STATUS_PRINTER_BUSY, #</DevStatusFaxMemoryFullReceive>
1047     -13849: STATUS_PRINTER_BUSY, #</DevStatusFaxReceiveError>
1048
1049 }
1050
1051 def StatusType6(dev): #  LaserJet Status (XML)
1052     info_device_status = cStringIO.StringIO()
1053     info_ssp = cStringIO.StringIO()
1054
1055     try:
1056         dev.getEWSUrl("/hp/device/info_device_status.xml", info_device_status)
1057         dev.getEWSUrl("/hp/device/info_ssp.xml", info_ssp)
1058     except:
1059         pass
1060
1061     info_device_status = info_device_status.getvalue()
1062     info_ssp = info_ssp.getvalue()
1063
1064     device_status = {}
1065     ssp = {}
1066
1067     if info_device_status:
1068         try:
1069             log.debug_block("info_device_status", info_device_status)
1070             device_status = utils.XMLToDictParser().parseXML(info_device_status)
1071             log.debug(device_status)
1072         except expat.ExpatError:
1073             log.error("Device Status XML parse error")
1074             device_status = {}
1075
1076     if info_ssp:
1077         try:
1078             log.debug_block("info_spp", info_ssp)
1079             ssp = utils.XMLToDictParser().parseXML(info_ssp)
1080             log.debug(ssp)
1081         except expat.ExpatError:
1082             log.error("SSP XML parse error")
1083             ssp = {}
1084
1085     status_code = device_status.get('devicestatuspage-devicestatus-statuslist-status-code-0', 0)
1086
1087     if not status_code:
1088         status_code = ssp.get('devicestatuspage-devicestatus-statuslist-status-code-0', 0)
1089
1090     black_supply_level = device_status.get('devicestatuspage-suppliesstatus-blacksupply-percentremaining', 0)
1091     black_supply_low = ssp.get('suppliesstatuspage-blacksupply-lowreached', 0)
1092     agents = []
1093
1094     agents.append({  'kind' : AGENT_KIND_TONER_CARTRIDGE,
1095                      'type' : AGENT_TYPE_BLACK,
1096                      'health' : 0,
1097                      'level' : black_supply_level,
1098                      'level-trigger' : 0,
1099                   })
1100
1101     if dev.tech_type == TECH_TYPE_COLOR_LASER:
1102         cyan_supply_level = device_status.get('devicestatuspage-suppliesstatus-cyansupply-percentremaining', 0)
1103         agents.append({  'kind' : AGENT_KIND_TONER_CARTRIDGE,
1104                          'type' : AGENT_TYPE_CYAN,
1105                          'health' : 0,
1106                          'level' : cyan_supply_level,
1107                          'level-trigger' : 0,
1108                       })
1109
1110         magenta_supply_level = device_status.get('devicestatuspage-suppliesstatus-magentasupply-percentremaining', 0)
1111         agents.append({  'kind' : AGENT_KIND_TONER_CARTRIDGE,
1112                          'type' : AGENT_TYPE_MAGENTA,
1113                          'health' : 0,
1114                          'level' : magenta_supply_level,
1115                          'level-trigger' : 0,
1116                       })
1117
1118         yellow_supply_level = device_status.get('devicestatuspage-suppliesstatus-yellowsupply-percentremaining', 0)
1119         agents.append({  'kind' : AGENT_KIND_TONER_CARTRIDGE,
1120                          'type' : AGENT_TYPE_YELLOW,
1121                          'health' : 0,
1122                          'level' : yellow_supply_level,
1123                          'level-trigger' : 0,
1124                       })
1125
1126     return {'revision' :    STATUS_REV_UNKNOWN,
1127              'agents' :      agents,
1128              'top-door' :    0,
1129              'supply-door' : 0,
1130              'duplexer' :    1,
1131              'photo-tray' :  0,
1132              'in-tray1' :    1,
1133              'in-tray2' :    1,
1134              'media-path' :  1,
1135              'status-code' : TYPE6_STATUS_CODE_MAP.get(status_code, STATUS_PRINTER_IDLE),
1136            }
1137
1138 # PJL status codes
1139 PJL_STATUS_MAP = {
1140     10001: STATUS_PRINTER_IDLE, # online
1141     10002: STATUS_PRINTER_OFFLINE, # offline
1142     10003: STATUS_PRINTER_WARMING_UP,
1143     10004: STATUS_PRINTER_BUSY, # self test
1144     10005: STATUS_PRINTER_BUSY, # reset
1145     10006: STATUS_PRINTER_LOW_TONER,
1146     10007: STATUS_PRINTER_CANCELING,
1147     10010: STATUS_PRINTER_SERVICE_REQUEST,
1148     10011: STATUS_PRINTER_OFFLINE,
1149     10013: STATUS_PRINTER_BUSY,
1150     10014: STATUS_PRINTER_REPORT_PRINTING,
1151     10015: STATUS_PRINTER_BUSY,
1152     10016: STATUS_PRINTER_BUSY,
1153     10017: STATUS_PRINTER_REPORT_PRINTING,
1154     10018: STATUS_PRINTER_BUSY,
1155     10019: STATUS_PRINTER_BUSY,
1156     10020: STATUS_PRINTER_BUSY,
1157     10021: STATUS_PRINTER_BUSY,
1158     10022: STATUS_PRINTER_REPORT_PRINTING,
1159     10023: STATUS_PRINTER_PRINTING,
1160     10024: STATUS_PRINTER_SERVICE_REQUEST,
1161     10025: STATUS_PRINTER_SERVICE_REQUEST,
1162     10026: STATUS_PRINTER_BUSY,
1163     10027: STATUS_PRINTER_MEDIA_JAM,
1164     10028: STATUS_PRINTER_REPORT_PRINTING,
1165     10029: STATUS_PRINTER_PRINTING,
1166     10030: STATUS_PRINTER_BUSY,
1167     10031: STATUS_PRINTER_BUSY,
1168     10032: STATUS_PRINTER_BUSY,
1169     10033: STATUS_PRINTER_SERVICE_REQUEST,
1170     10034: STATUS_PRINTER_CANCELING,
1171     10035: STATUS_PRINTER_PRINTING,
1172     10036: STATUS_PRINTER_WARMING_UP,
1173     10200: STATUS_PRINTER_LOW_BLACK_TONER,
1174     10201: STATUS_PRINTER_LOW_CYAN_TONER,
1175     10202: STATUS_PRINTER_LOW_MAGENTA_TONER,
1176     10203: STATUS_PRINTER_LOW_YELLOW_TONER,
1177     10204: STATUS_PRINTER_LOW_TONER, # order image drum
1178     10205: STATUS_PRINTER_LOW_BLACK_TONER, # order black drum
1179     10206: STATUS_PRINTER_LOW_CYAN_TONER, # order cyan drum
1180     10207: STATUS_PRINTER_LOW_MAGENTA_TONER, # order magenta drum
1181     10208: STATUS_PRINTER_LOW_YELLOW_TONER, # order yellow drum
1182     10209: STATUS_PRINTER_LOW_BLACK_TONER,
1183     10210: STATUS_PRINTER_LOW_CYAN_TONER,
1184     10211: STATUS_PRINTER_LOW_MAGENTA_TONER,
1185     10212: STATUS_PRINTER_LOW_YELLOW_TONER,
1186     10213: STATUS_PRINTER_SERVICE_REQUEST, # order transport kit
1187     10214: STATUS_PRINTER_SERVICE_REQUEST, # order cleaning kit
1188     10215: STATUS_PRINTER_SERVICE_REQUEST, # order transfer kit
1189     10216: STATUS_PRINTER_SERVICE_REQUEST, # order fuser kit
1190     10217: STATUS_PRINTER_SERVICE_REQUEST, # maintenance
1191     10218: STATUS_PRINTER_LOW_TONER,
1192     10300: STATUS_PRINTER_LOW_BLACK_TONER, # replace black toner
1193     10301: STATUS_PRINTER_LOW_CYAN_TONER, # replace cyan toner
1194     10302: STATUS_PRINTER_LOW_MAGENTA_TONER, # replace magenta toner
1195     10303: STATUS_PRINTER_LOW_YELLOW_TONER, # replace yellow toner
1196     10304: STATUS_PRINTER_SERVICE_REQUEST, # replace image drum
1197     10305: STATUS_PRINTER_SERVICE_REQUEST, # replace black drum
1198     10306: STATUS_PRINTER_SERVICE_REQUEST, # replace cyan drum
1199     10307: STATUS_PRINTER_SERVICE_REQUEST, # replace magenta drum
1200     10308: STATUS_PRINTER_SERVICE_REQUEST, # replace yellow drum
1201     10309: STATUS_PRINTER_SERVICE_REQUEST, # replace black cart
1202     10310: STATUS_PRINTER_SERVICE_REQUEST, # replace cyan cart
1203     10311: STATUS_PRINTER_SERVICE_REQUEST, # replace magenta cart
1204     10312: STATUS_PRINTER_SERVICE_REQUEST, # replace yellow cart
1205     10313: STATUS_PRINTER_SERVICE_REQUEST, # replace transport kit
1206     10314: STATUS_PRINTER_SERVICE_REQUEST, # replace cleaning kit
1207     10315: STATUS_PRINTER_SERVICE_REQUEST, # replace transfer kit
1208     10316: STATUS_PRINTER_SERVICE_REQUEST, # replace fuser kit
1209     10317: STATUS_PRINTER_SERVICE_REQUEST,
1210     10318: STATUS_PRINTER_SERVICE_REQUEST, # replace supplies
1211     10400: STATUS_PRINTER_NON_HP_INK, # [sic]
1212     10401: STATUS_PRINTER_IDLE,
1213     10402: STATUS_PRINTER_SERVICE_REQUEST,
1214     10403: STATUS_PRINTER_IDLE,
1215     # 11xyy - Background paper-loading
1216     # 12xyy - Background paper-tray status
1217     # 15xxy - Output-bin status
1218     # 20xxx - PJL parser errors
1219     # 25xxx - PJL parser warnings
1220     # 27xxx - PJL semantic errors
1221     # 30xxx - Auto continuable conditions
1222     30119: STATUS_PRINTER_MEDIA_JAM,
1223     # 32xxx - PJL file system errors
1224     # 35xxx - Potential operator intervention conditions
1225     # 40xxx - Operator intervention conditions
1226     40021: STATUS_PRINTER_DOOR_OPEN,
1227     40022: STATUS_PRINTER_MEDIA_JAM,
1228     40038: STATUS_PRINTER_LOW_TONER,
1229     40600: STATUS_PRINTER_NO_TONER,
1230     # 41xyy - Foreground paper-loading messages
1231     # 43xyy - Optional paper handling device messages
1232     # 44xyy - LJ 4xxx/5xxx paper jam messages
1233     # 50xxx - Hardware errors
1234     # 55xxx - Personality errors
1235
1236 }
1237
1238 MIN_PJL_ERROR_CODE = 10001
1239 DEFAULT_PJL_ERROR_CODE = 10001
1240
1241 def MapPJLErrorCode(error_code, str_code=None):
1242     if error_code < MIN_PJL_ERROR_CODE:
1243         return STATUS_PRINTER_BUSY
1244
1245     if str_code is None:
1246         str_code = str(error_code)
1247
1248     if len(str_code) < 5:
1249         return STATUS_PRINTER_BUSY
1250
1251     status_code = PJL_STATUS_MAP.get(error_code, None)
1252
1253     if status_code is None:
1254         status_code = STATUS_PRINTER_BUSY
1255
1256         if 10999 < error_code < 12000: # 11xyy - Background paper-loading
1257             # x = tray #
1258             # yy = media code
1259             tray = int(str_code[2])
1260             media = int(str_code[3:])
1261             log.debug("Background paper loading for tray #%d" % tray)
1262             log.debug("Media code = %d" % media)
1263
1264         elif 11999 < error_code < 13000: # 12xyy - Background paper-tray status
1265             # x = tray #
1266             # yy = status code
1267             tray = int(str_code[2])
1268             status = int(str_code[3:])
1269             log.debug("Background paper tray status for tray #%d" % tray)
1270             log.debug("Status code = %d" % status)
1271
1272         elif 14999 < error_code < 16000: # 15xxy - Output-bin status
1273             # xx = output bin
1274             # y = status code
1275             bin = int(str_code[2:4])
1276             status = int(str_code[4])
1277             log.debug("Output bin full for bin #%d" % bin)
1278             status_code = STATUS_PRINTER_OUTPUT_BIN_FULL
1279
1280         elif 19999 < error_code < 28000: # 20xxx, 25xxx, 27xxx PJL errors
1281             status_code = STATUS_PRINTER_SERVICE_REQUEST
1282
1283         elif 29999 < error_code < 31000: # 30xxx - Auto continuable conditions
1284             log.debug("Auto continuation condition #%d" % error_code)
1285             status_code = STATUS_PRINTER_BUSY
1286
1287         elif 34999 < error_code < 36000: # 35xxx - Potential operator intervention conditions
1288             status_code = STATUS_PRINTER_SERVICE_REQUEST
1289
1290         elif 39999 < error_code < 41000: # 40xxx - Operator intervention conditions
1291             status_code = STATUS_PRINTER_SERVICE_REQUEST
1292
1293         elif 40999 < error_code < 42000: # 41xyy - Foreground paper-loading messages
1294             # x = tray
1295             # yy = media code
1296             tray = int(str_code[2])
1297             media = int(str_code[3:])
1298             log.debug("Foreground paper loading for tray #%d" % tray)
1299             log.debug("Media code = %d" % media)
1300             status_code = STATUS_PRINTER_OUT_OF_PAPER
1301
1302         elif 41999 < error_code < 43000:
1303             status_code = STATUS_PRINTER_MEDIA_JAM
1304
1305         elif 42999 < error_code < 44000: # 43xyy - Optional paper handling device messages
1306             status_code = STATUS_PRINTER_SERVICE_REQUEST
1307
1308         elif 43999 < error_code < 45000: # 44xyy - LJ 4xxx/5xxx paper jam messages
1309             status_code = STATUS_PRINTER_MEDIA_JAM
1310
1311         elif 49999 < error_code < 51000: # 50xxx - Hardware errors
1312             status_code = STATUS_PRINTER_HARD_ERROR
1313
1314         elif 54999 < error_code < 56000 : # 55xxx - Personality errors
1315             status_code = STATUS_PRINTER_HARD_ERROR
1316
1317     log.debug("Mapped PJL error code %d to status code %d" % (error_code, status_code))
1318     return status_code
1319
1320
1321 pjl_code_pat = re.compile("""^CODE\s*=\s*(\d.*)$""", re.IGNORECASE)
1322
1323
1324
1325 def StatusType8(dev): #  LaserJet PJL (B&W only)
1326     try:
1327         # Will error if printer is busy printing...
1328         dev.openPrint()
1329     except Error, e:
1330         log.warn(e.msg)
1331         status_code = STATUS_PRINTER_BUSY
1332     else:
1333         try:
1334             try:
1335                 dev.writePrint("\x1b%-12345X@PJL INFO STATUS \r\n\x1b%-12345X")
1336                 pjl_return = dev.readPrint(1024, timeout=5, allow_short_read=True)
1337                 dev.close()
1338
1339                 log.debug_block("PJL return:", pjl_return)
1340
1341                 str_code = '10001'
1342
1343                 for line in pjl_return.splitlines():
1344                     line = line.strip()
1345                     match = pjl_code_pat.match(line)
1346
1347                     if match is not None:
1348                         str_code = match.group(1)
1349                         break
1350
1351                 log.debug("Code = %s" % str_code)
1352
1353                 try:
1354                     error_code = int(str_code)
1355                 except ValueError:
1356                     error_code = DEFAULT_PJL_ERROR_CODE
1357
1358                 log.debug("Error code = %d" % error_code)
1359
1360                 status_code = MapPJLErrorCode(error_code, str_code)
1361             except Error:
1362                 status_code = STATUS_PRINTER_HARD_ERROR
1363         finally:
1364             try:
1365                 dev.closePrint()
1366             except Error:
1367                 pass
1368
1369     agents = []
1370
1371     # TODO: Only handles mono lasers...
1372     if status_code in (STATUS_PRINTER_LOW_TONER, STATUS_PRINTER_LOW_BLACK_TONER):
1373         health = AGENT_HEALTH_OK
1374         level_trigger = AGENT_LEVEL_TRIGGER_MAY_BE_LOW
1375         level = 0
1376
1377     elif status_code == STATUS_PRINTER_NO_TONER:
1378         health = AGENT_HEALTH_MISINSTALLED
1379         level_trigger = AGENT_LEVEL_TRIGGER_MAY_BE_LOW
1380         level = 0
1381
1382     else:
1383         health = AGENT_HEALTH_OK
1384         level_trigger = AGENT_LEVEL_TRIGGER_SUFFICIENT_0
1385         level = 100
1386
1387     log.debug("Agent: health=%d, level=%d, trigger=%d" % (health, level, level_trigger))
1388
1389     agents.append({  'kind' : AGENT_KIND_TONER_CARTRIDGE,
1390                      'type' : AGENT_TYPE_BLACK,
1391                      'health' : health,
1392                      'level' : level,
1393                      'level-trigger' : level_trigger,
1394                   })
1395
1396     if dev.tech_type == TECH_TYPE_COLOR_LASER:
1397         level = 100
1398         level_trigger = AGENT_LEVEL_TRIGGER_SUFFICIENT_0
1399         if status_code == STATUS_PRINTER_LOW_CYAN_TONER:
1400             level = 0
1401             level_trigger = AGENT_LEVEL_TRIGGER_MAY_BE_LOW
1402
1403         log.debug("Agent: health=%d, level=%d, trigger=%d" % (health, level, level_trigger))
1404
1405         agents.append({  'kind' : AGENT_KIND_TONER_CARTRIDGE,
1406                          'type' : AGENT_TYPE_CYAN,
1407                          'health' : AGENT_HEALTH_OK,
1408                          'level' : level,
1409                          'level-trigger' : level_trigger,
1410                       })
1411
1412         level = 100
1413         level_trigger = AGENT_LEVEL_TRIGGER_SUFFICIENT_0
1414         if status_code == STATUS_PRINTER_LOW_MAGENTA_TONER:
1415             level = 0
1416             level_trigger = AGENT_LEVEL_TRIGGER_MAY_BE_LOW
1417
1418         log.debug("Agent: health=%d, level=%d, trigger=%d" % (health, level, level_trigger))
1419
1420         agents.append({  'kind' : AGENT_KIND_TONER_CARTRIDGE,
1421                          'type' : AGENT_TYPE_MAGENTA,
1422                          'health' : AGENT_HEALTH_OK,
1423                          'level' : level,
1424                          'level-trigger' : level_trigger,
1425                       })
1426
1427         level = 100
1428         level_trigger = AGENT_LEVEL_TRIGGER_SUFFICIENT_0
1429         if status_code == STATUS_PRINTER_LOW_YELLOW_TONER:
1430             level = 0
1431             level_trigger = AGENT_LEVEL_TRIGGER_MAY_BE_LOW
1432
1433         log.debug("Agent: health=%d, level=%d, trigger=%d" % (health, level, level_trigger))
1434
1435         agents.append({  'kind' : AGENT_KIND_TONER_CARTRIDGE,
1436                          'type' : AGENT_TYPE_YELLOW,
1437                          'health' : AGENT_HEALTH_OK,
1438                          'level' : level,
1439                          'level-trigger' : level_trigger,
1440                       })
1441
1442     if status_code == 40021:
1443         top_door = 0
1444     else:
1445         top_door = 1
1446
1447     log.debug("Status code = %d" % status_code)
1448
1449     return { 'revision' :    STATUS_REV_UNKNOWN,
1450              'agents' :      agents,
1451              'top-door' :    top_door,
1452              'supply-door' : top_door,
1453              'duplexer' :    0,
1454              'photo-tray' :  0,
1455              'in-tray1' :    1,
1456              'in-tray2' :    1,
1457              'media-path' :  1,
1458              'status-code' : status_code,
1459            }
1460
1461
1462 element_type10_xlate = { 'ink' : AGENT_KIND_SUPPLY,
1463                          'inkCartridge' : AGENT_KIND_HEAD_AND_SUPPLY,
1464                          'printhead' : AGENT_KIND_HEAD,
1465                          'toner' : AGENT_KIND_TONER_CARTRIDGE,
1466                          'tonerCartridge' : AGENT_KIND_TONER_CARTRIDGE,
1467                        }
1468
1469 pen_type10_xlate = { 'pK' : AGENT_TYPE_PG,
1470                      'CMY' : AGENT_TYPE_CMY,
1471                      'M' : AGENT_TYPE_MAGENTA,
1472                      'C' : AGENT_TYPE_CYAN,
1473                      'Y' : AGENT_TYPE_YELLOW,
1474                      'K' : AGENT_TYPE_BLACK,
1475                    }
1476
1477 pen_level10_xlate = { 'ok' : AGENT_LEVEL_TRIGGER_SUFFICIENT_0,
1478                       'low' : AGENT_LEVEL_TRIGGER_MAY_BE_LOW,
1479                       'out' : AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT,
1480                       'empty' : AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT,
1481                       'missing' : AGENT_LEVEL_TRIGGER_ALMOST_DEFINITELY_OUT,
1482                     }
1483
1484 pen_health10_xlate = { 'ok' : AGENT_HEALTH_OK,
1485                        'misinstalled' : AGENT_HEALTH_MISINSTALLED,
1486                        'missing' : AGENT_HEALTH_MISINSTALLED,
1487                      }
1488
1489 def clean(data):
1490     if data[0] is not '<':
1491         size = -1
1492         temp = ""
1493         while size:
1494             index = data.find('\r\n')
1495             size = int(data[0:index+1], 16)
1496             temp = temp + data[index+2:index+2+size]
1497             data = data[index+2+size+2:len(data)]
1498         data = temp
1499     return data
1500
1501 def StatusType10FetchUrl(func, url, footer=""):
1502     data_fp = cStringIO.StringIO()
1503     if footer:
1504         #data = dev.getEWSUrl_LEDM(url, data_fp, footer)
1505         data = func(url, data_fp, footer)
1506     else:
1507         #data = dev.getEWSUrl_LEDM(url, data_fp)
1508         data = func(url, data_fp)
1509         if data:
1510             data = data.split('\r\n\r\n', 1)[1]
1511             if data:
1512                 data = clean(data)
1513     return data
1514
1515 def StatusType10(func): # Low End Data Model
1516     status_block = { 'revision' :    STATUS_REV_UNKNOWN,
1517                      'agents' :      [],
1518                      'top-door' :    TOP_DOOR_NOT_PRESENT,
1519                      'supply-door' : TOP_DOOR_NOT_PRESENT,
1520                      'duplexer' :    DUPLEXER_NOT_PRESENT,
1521                      'photo-tray' :  PHOTO_TRAY_NOT_PRESENT,
1522                      'in-tray1' :    IN_TRAY_NOT_PRESENT,
1523                      'in-tray2' :    IN_TRAY_NOT_PRESENT,
1524                      'media-path' :  MEDIA_PATH_NOT_PRESENT,
1525                      'status-code' : STATUS_PRINTER_IDLE,
1526                    }
1527
1528     if not etree_loaded and not elementtree_loaded:
1529         log.error("cannot get status for printer. please load ElementTree module")
1530         return status_block
1531
1532     # Get the dynamic consumables configuration
1533     data = StatusType10FetchUrl(func, "/DevMgmt/ConsumableConfigDyn.xml")
1534     if not data:
1535         return status_block
1536     data = data.replace("ccdyn:", "").replace("dd:", "")
1537
1538     # Parse the agent status XML
1539     agents = []
1540     try:
1541         if etree_loaded:
1542             tree = ElementTree.XML(data)
1543         if not etree_loaded and elementtree_loaded:
1544             tree = XML(data)
1545         elements = tree.findall("ConsumableInfo")
1546         for e in elements:
1547             health = AGENT_HEALTH_OK
1548             ink_level = 0
1549             try:
1550                 type = e.find("ConsumableTypeEnum").text
1551                 state = e.find("ConsumableLifeState/ConsumableState").text
1552
1553                 # level
1554                 if type == "ink" or type == "inkCartridge" or type == "toner" or type == "tonerCartridge":
1555                     ink_type = e.find("ConsumableLabelCode").text
1556                     if state != "missing":
1557                         try:
1558                            ink_level = int(e.find("ConsumablePercentageLevelRemaining").text)
1559                         except:
1560                            ink_level = 0
1561                 else:
1562                     ink_type = ''
1563                     if state == "ok":
1564                         ink_level = 100
1565
1566                 log.debug("type '%s' state '%s' ink_type '%s' ink_level %d" % (type, state, ink_type, ink_level))
1567
1568                 entry = { 'kind' : element_type10_xlate.get(type, AGENT_KIND_NONE),
1569                           'type' : pen_type10_xlate.get(ink_type, AGENT_TYPE_NONE),
1570                           'health' : pen_health10_xlate.get(state, AGENT_HEALTH_OK),
1571                           'level' : int(ink_level),
1572                           'level-trigger' : pen_level10_xlate.get(state, AGENT_LEVEL_TRIGGER_SUFFICIENT_0)
1573                         }
1574
1575                 log.debug("%s" % entry)
1576                 agents.append(entry)
1577             except AttributeError:
1578                 log.debug("no value found for attribute")
1579     except (expat.ExpatError, UnboundLocalError):
1580         agents = []
1581     status_block['agents'] = agents
1582
1583     # Get the media handling configuration
1584     data = StatusType10FetchUrl(func, "/DevMgmt/MediaHandlingDyn.xml")
1585     if not data:
1586         return status_block
1587     data = data.replace("mhdyn:", "").replace("dd:", "")
1588
1589     # Parse the media handling XML
1590     try:
1591         if etree_loaded:
1592             tree = ElementTree.XML(data)
1593         if not etree_loaded and elementtree_loaded:
1594             tree = XML(data)
1595         elements = tree.findall("InputTray")
1596     except (expat.ExpatError, UnboundLocalError):
1597         elements = []
1598     for e in elements:
1599         bin_name = e.find("InputBin").text
1600         if bin_name == "Tray1":
1601             status_block['in-tray1'] = IN_TRAY_PRESENT
1602         elif bin_name == "Tray2":
1603             status_block['in-tray2'] = IN_TRAY_PRESENT
1604         elif bin_name == "PhotoTray":
1605             status_block['photo-tray'] = PHOTO_TRAY_ENGAGED
1606         else:
1607             log.error("found invalid bin name '%s'" % bin_name)
1608
1609     try:
1610         elements = tree.findall("Accessories/MediaHandlingDeviceFunctionType")
1611     except UnboundLocalError:
1612         elements = []
1613     for e in elements:
1614         if e.text == "autoDuplexor":
1615             status_block['duplexer'] = DUPLEXER_DOOR_CLOSED
1616
1617     # Get the product status
1618     data = StatusType10FetchUrl(func, "/DevMgmt/ProductStatusDyn.xml")
1619     if not data:
1620         return status_block
1621     data = data.replace("psdyn:", "").replace("locid:", "")
1622     data = data.replace("pscat:", "").replace("dd:", "").replace("ad:", "")
1623
1624     # Parse the product status XML
1625     try:
1626         if etree_loaded:
1627             tree = ElementTree.XML(data)
1628         if not etree_loaded and elementtree_loaded:
1629             tree = XML(data)
1630         elements = tree.findall("Status/StatusCategory")
1631     except (expat.ExpatError, UnboundLocalError):
1632         elements = []
1633     for e in elements:
1634         if e.text == "processing":
1635             status_block['status-code'] = STATUS_PRINTER_PRINTING
1636         if e.text == "closeDoorOrCover":
1637             status_block['status-code'] = STATUS_PRINTER_DOOR_OPEN
1638         elif e.text == "shuttingDown":
1639             status_block['status-code'] = STATUS_PRINTER_TURNING_OFF
1640         elif e.text == "cancelJob":
1641             status_block['status-code'] = STATUS_PRINTER_CANCELING
1642         elif e.text == "trayEmptyOrOpen":
1643             status_block['status-code'] = STATUS_PRINTER_OUT_OF_PAPER
1644         elif e.text == "jamInPrinter":
1645             status_block['status-code'] = STATUS_PRINTER_MEDIA_JAM
1646         elif e.text == "hardError":
1647             status_block['status-code'] = STATUS_PRINTER_HARD_ERROR
1648         elif e.text == "outputBinFull":
1649             status_block['status-code'] = STATUS_PRINTER_OUTPUT_BIN_FULL
1650         elif e.text == "unexpectedSizeInTray" or e.text == "sizeMismatchInTray":
1651             status_block['status-code'] = STATUS_PRINTER_MEDIA_SIZE_MISMATCH
1652         elif e.text == "insertOrCloseTray2":
1653             status_block['status-code'] = STATUS_PRINTER_TRAY_2_MISSING
1654         elif e.text == "scannerError":
1655             status_block['status-code'] = EVENT_SCANNER_FAIL
1656         elif e.text == "scanProcessing":
1657             status_block['status-code'] = EVENT_START_SCAN_JOB
1658         elif e.text == "scannerAdfLoaded":
1659             status_block['status-code'] = EVENT_SCAN_ADF_LOADED
1660         elif e.text == "scanToDestinationNotSet":
1661             status_block['status-code'] = EVENT_SCAN_TO_DESTINATION_NOTSET
1662         elif e.text == "scanWaitingForPC":
1663             status_block['status-code'] = EVENT_SCAN_WAITING_FOR_PC
1664         elif e.text == "scannerAdfJam":
1665             status_block['status-code'] = EVENT_SCAN_ADF_JAM
1666         elif e.text == "scannerAdfDoorOpen":
1667             status_block['status-code'] = EVENT_SCAN_ADF_DOOR_OPEN
1668         elif e.text == "faxProcessing":
1669             status_block['status-code'] = EVENT_START_FAX_JOB
1670         elif e.text == "faxSending":
1671             status_block['status-code'] = STATUS_FAX_TX_ACTIVE
1672         elif e.text == "faxReceiving":
1673             status_block['status-code'] = STATUS_FAX_RX_ACTIVE
1674         elif e.text == "faxDialing":
1675             status_block['status-code'] = EVENT_FAX_DIALING
1676         elif e.text == "faxConnecting":
1677             status_block['status-code'] = EVENT_FAX_CONNECTING
1678         elif e.text == "faxSendError":
1679             status_block['status-code'] = EVENT_FAX_SEND_ERROR
1680         elif e.text == "faxErrorStorageFull":
1681             status_block['status-code'] = EVENT_FAX_ERROR_STORAGE_FULL
1682         elif e.text == "faxReceiveError":
1683             status_block['status-code'] = EVENT_FAX_RECV_ERROR
1684         elif e.text == "faxBlocking":
1685             status_block['status-code'] = EVENT_FAX_BLOCKING
1686         elif e.text == "inPowerSave":
1687             status_block['status-code'] = STATUS_PRINTER_POWER_SAVE
1688         elif e.text == "incorrectCartridge":
1689             status_block['status-code'] = STATUS_PRINTER_CARTRIDGE_WRONG
1690         elif e.text == "cartridgeMissing":
1691             status_block['status-code'] = STATUS_PRINTER_CARTRIDGE_MISSING
1692         elif e.text == "missingPrintHead":
1693             status_block['status-code'] = STATUS_PRINTER_PRINTHEAD_MISSING
1694
1695     return status_block