Browse Source

[FIX] Python3 Compatible

pull/79/merge
Sreejith P 7 years ago
parent
commit
1f98176f0e
  1. 35
      oh_hr_zk_attendance/README.rst
  2. 22
      oh_hr_zk_attendance/__init__.py
  3. 45
      oh_hr_zk_attendance/__manifest__.py
  4. 15
      oh_hr_zk_attendance/data/download_data.xml
  5. 25
      oh_hr_zk_attendance/models/__init__.py
  6. 105
      oh_hr_zk_attendance/models/machine_analysis.py
  7. 280
      oh_hr_zk_attendance/models/zk_machine.py
  8. 119
      oh_hr_zk_attendance/models/zkattendance.py
  9. 64
      oh_hr_zk_attendance/models/zkconnect.py
  10. 71
      oh_hr_zk_attendance/models/zkconst.py
  11. 82
      oh_hr_zk_attendance/models/zkdevice.py
  12. 56
      oh_hr_zk_attendance/models/zkextendfmt.py
  13. 62
      oh_hr_zk_attendance/models/zkextendoplog.py
  14. 44
      oh_hr_zk_attendance/models/zkface.py
  15. 156
      oh_hr_zk_attendance/models/zklib.py
  16. 23
      oh_hr_zk_attendance/models/zkos.py
  17. 23
      oh_hr_zk_attendance/models/zkpin.py
  18. 43
      oh_hr_zk_attendance/models/zkplatform.py
  19. 23
      oh_hr_zk_attendance/models/zkserialnumber.py
  20. 23
      oh_hr_zk_attendance/models/zkssr.py
  21. 50
      oh_hr_zk_attendance/models/zktime.py
  22. 140
      oh_hr_zk_attendance/models/zkuser.py
  23. 23
      oh_hr_zk_attendance/models/zkversion.py
  24. 23
      oh_hr_zk_attendance/models/zkworkcode.py
  25. 4
      oh_hr_zk_attendance/security/ir.model.access.csv
  26. BIN
      oh_hr_zk_attendance/static/description/attendance_log.png
  27. BIN
      oh_hr_zk_attendance/static/description/banner.gif
  28. BIN
      oh_hr_zk_attendance/static/description/cybro_logo.png
  29. BIN
      oh_hr_zk_attendance/static/description/devicelog.png
  30. BIN
      oh_hr_zk_attendance/static/description/employee.png
  31. BIN
      oh_hr_zk_attendance/static/description/icon.png
  32. 164
      oh_hr_zk_attendance/static/description/index.html
  33. BIN
      oh_hr_zk_attendance/static/description/zk_config.png
  34. BIN
      oh_hr_zk_attendance/static/description/zk_warn2.png
  35. BIN
      oh_hr_zk_attendance/static/description/zk_warning.png
  36. 71
      oh_hr_zk_attendance/views/zk_machine_attendance_view.xml
  37. 69
      oh_hr_zk_attendance/views/zk_machine_view.xml

35
oh_hr_zk_attendance/README.rst

@ -0,0 +1,35 @@
Biometric Device Integration v11
================================
This Cybrosys's module integrates Odoo attendance with biometric device attendance.
Features
========
* Ingrates biometric device(Face+Thumb) with HR attendance.
* Managing attendance automatically
* Keeps zk machine history in Odoo
* Option to configure multiple zk devices
* Option to clear all zk history from both device and Odoo
Technical Notes
===============
Used Libraries:
*This integration is only applicable for the the device ZKteco model 'uFace 202' & 'iFace990'
* zklib
you can install zklib library using "sudo pip install zklib"
Compatible Devices
*ZKteco model 'uFace 202'
*ZKteco model 'iFace990'
Author
=======
* Cybrosys Techno Solutions <https://www.cybrosys.com>
Credits
=======
Developer: Niyas Raphy @ Cybrosys, niyas@cybrosys.in
Developer: Jesni Banu @ cybrosys, jesni@cybrosys.in
Developer: Mostafa Shokiel , mostafa.shokiel@gmail.com

22
oh_hr_zk_attendance/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<https://www.cybrosys.com>)
#
# This program is free software: you can modify
# it under the terms of the GNU Affero General Public License (AGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###################################################################################
from . import models

45
oh_hr_zk_attendance/__manifest__.py

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<https://www.cybrosys.com>)
#
# This program is free software: you can modify
# it under the terms of the GNU Affero General Public License (AGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###################################################################################
{
'name': 'Biometric Device Integration',
'version': '11.0.1.0.0',
'summary': """Integrating Biometric Device With HR Attendance (Face + Thumb)""",
'description': 'This module integrates Odoo with the biometric device(Model: ZKteco uFace 202)',
'category': 'Generic Modules/Human Resources',
'author': 'Cybrosys Techno Solutions, Mostafa Shokiel',
'company': 'Cybrosys Techno Solutions',
'website': "http://www.cybrosys.com",
'depends': ['base_setup', 'hr_attendance'],
'data': [
'security/ir.model.access.csv',
'views/zk_machine_view.xml',
'views/zk_machine_attendance_view.xml',
'data/download_data.xml'
],
'images': ['static/description/banner.gif'],
'license': 'AGPL-3',
'demo': [],
'installable': True,
'auto_install': False,
'application': False,
}

15
oh_hr_zk_attendance/data/download_data.xml

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<odoo noupdate="1">
<record forcecreate="True" id="cron_download_data" model="ir.cron">
<field name="name">Download Data</field>
<field eval="True" name="active" />
<field name="user_id" ref="base.user_root" />
<field name="interval_number">10</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="model_id" ref="oh_hr_zk_attendance.model_zk_machine" />
<field name="state">code</field>
<field name="code">model.cron_download()</field>
</record>
</odoo>

25
oh_hr_zk_attendance/models/__init__.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<https://www.cybrosys.com>)
#
# This program is free software: you can modify
# it under the terms of the GNU Affero General Public License (AGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###################################################################################
from . import zk_machine
from . import machine_analysis
from . import zklib

105
oh_hr_zk_attendance/models/machine_analysis.py

@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<https://www.cybrosys.com>)
#
# This program is free software: you can modify
# it under the terms of the GNU Affero General Public License (AGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###################################################################################
from odoo import tools
from odoo import models, fields, api, _
class HrEmployee(models.Model):
_inherit = 'hr.employee'
device_id = fields.Char(string='Biometric Device ID')
class ZkMachine(models.Model):
_name = 'zk.machine.attendance'
_inherit = 'hr.attendance'
@api.constrains('check_in', 'check_out', 'employee_id')
def _check_validity(self):
"""overriding the __check_validity function for employee attendance."""
pass
device_id = fields.Char(string='Biometric Device ID')
punch_type = fields.Selection([('0', 'Check In'),
('1', 'Check Out'),
('2', 'Break Out'),
('3', 'Break In'),
('4', 'Overtime In'),
('5', 'Overtime Out')],
string='Punching Type')
attendance_type = fields.Selection([('1', 'Finger'),
('15', 'Face'),
('2','Type_2'),
('3','Password'),
('4','Card')], string='Category')
punching_time = fields.Datetime(string='Punching Time')
address_id = fields.Many2one('res.partner', string='Working Address')
class ReportZkDevice(models.Model):
_name = 'zk.report.daily.attendance'
_auto = False
_order = 'punching_day desc'
name = fields.Many2one('hr.employee', string='Employee')
punching_day = fields.Date(string='Date')
address_id = fields.Many2one('res.partner', string='Working Address')
attendance_type = fields.Selection([('1', 'Finger'),
('15', 'Face'),
('2','Type_2'),
('3','Password'),
('4','Card')],
string='Category')
punch_type = fields.Selection([('0', 'Check In'),
('1', 'Check Out'),
('2', 'Break Out'),
('3', 'Break In'),
('4', 'Overtime In'),
('5', 'Overtime Out')], string='Punching Type')
punching_time = fields.Datetime(string='Punching Time')
def init(self):
tools.drop_view_if_exists(self._cr, 'zk_report_daily_attendance')
self._cr.execute("""
create or replace view zk_report_daily_attendance as (
select
min(z.id) as id,
z.employee_id as name,
z.write_date as punching_day,
z.address_id as address_id,
z.attendance_type as attendance_type,
z.punching_time as punching_time,
z.punch_type as punch_type
from zk_machine_attendance z
join hr_employee e on (z.employee_id=e.id)
GROUP BY
z.employee_id,
z.write_date,
z.address_id,
z.attendance_type,
z.punch_type,
z.punching_time
)
""")

280
oh_hr_zk_attendance/models/zk_machine.py

@ -0,0 +1,280 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<https://www.cybrosys.com>)
#
# This program is free software: you can modify
# it under the terms of the GNU Affero General Public License (AGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###################################################################################
import pytz
import sys
import datetime
import logging
import binascii
from . import zklib
from .zkconst import *
from struct import unpack
from odoo import api, fields, models
from odoo import _
from odoo.exceptions import UserError, ValidationError
_logger = logging.getLogger(__name__)
class HrAttendance(models.Model):
_inherit = 'hr.attendance'
device_id = fields.Char(string='Biometric Device ID')
class ZkMachine(models.Model):
_name = 'zk.machine'
name = fields.Char(string='Machine IP', required=True)
port_no = fields.Integer(string='Port No', required=True)
address_id = fields.Many2one('res.partner', string='Working Address')
company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id.id)
@api.multi
def device_connect(self, zk):
command = CMD_CONNECT
command_string = ''
chksum = 0
session_id = 0
reply_id = -1 + USHRT_MAX
buf = zk.createHeader(command, chksum, session_id,
reply_id, command_string)
zk.zkclient.sendto(buf, zk.address)
try:
zk.data_recv, addr = zk.zkclient.recvfrom(1024)
zk.session_id = unpack('HHHH', zk.data_recv[:8])[2]
command = unpack('HHHH', zk.data_recv[:8])[0]
if command == 2005:
conn = True
else:
conn = False
except:
conn = False
return conn
@api.multi
def clear_attendance(self):
for info in self:
try:
machine_ip = info.name
port = info.port_no
zk = zklib.ZKLib(machine_ip, port)
conn = self.device_connect(zk)
if conn:
zk.enableDevice()
clear_data = zk.getAttendance()
if clear_data:
zk.clearAttendance()
self._cr.execute("""delete from zk_machine_attendance""")
else:
raise UserError(_('Unable to get the attendance log, please try again later.'))
else:
raise UserError(_('Unable to connect, please check the parameters and network connections.'))
except:
raise ValidationError('Warning !!! Machine is not connected')
def getSizeUser(self, zk):
"""Checks a returned packet to see if it returned CMD_PREPARE_DATA,
indicating that data packets are to be sent
Returns the amount of bytes that are going to be sent"""
command = unpack('HHHH', zk.data_recv[:8])[0]
if command == CMD_PREPARE_DATA:
size = unpack('I', zk.data_recv[8:12])[0]
return size
else:
return False
def zkgetuser(self, zk):
"""Start a connection with the time clock"""
command = CMD_USERTEMP_RRQ
command_string = '\x05'
chksum = 0
session_id = zk.session_id
reply_id = unpack('HHHH', zk.data_recv[:8])[3]
buf = zk.createHeader(command, chksum, session_id, reply_id, command_string)
zk.zkclient.sendto(buf, zk.address)
try:
zk.data_recv, addr = zk.zkclient.recvfrom(1024)
if self.getSizeUser(zk):
bytes = self.getSizeUser(zk)
while bytes > 0:
data_recv, addr = zk.zkclient.recvfrom(1032)
zk.userdata.append(data_recv)
bytes -= 1024
zk.session_id = unpack('HHHH', zk.data_recv[:8])[2]
data_recv = zk.zkclient.recvfrom(8)
users = {}
if len(zk.userdata) > 0:
userdata = zk.userdata[0]
userdata = userdata[11:]
while len(userdata) > 72:
uid, role, password, name, userid = unpack('2s2s8s28sx31s', userdata.ljust(72)[:72])
uid = int(binascii.hexlify(uid), 16)
# Clean up some messy characters from the user name
password = password.split(b'\x00', 1)[0]
password = str(password.strip(b'\x00|\x01\x10x|\x000').decode('utf-8'))
# uid = uid.split('\x00', 1)[0]
userid = str(userid.strip(b'\x00|\x01\x10x|\x000|\x9aC').decode('utf-8'))
name = name.split(b'\x00', 1)[0].decode('utf-8')
if name.strip() == "":
name = uid
users[uid] = (userid, name, int(binascii.hexlify(role), 16), password)
userdata = userdata[72:]
return users
except:
return False
@api.model
def cron_download(self):
machines = self.env['zk.machine'].search([])
for machine in machines :
machine.download_attendance()
@api.multi
def download_attendance(self):
_logger.info("++++++++++++Cron Executed++++++++++++++++++++++")
zk_attendance = self.env['zk.machine.attendance']
att_obj = self.env['hr.attendance']
for info in self:
machine_ip = info.name
port = info.port_no
zk = zklib.ZKLib(machine_ip, port)
conn = self.device_connect(zk)
if conn:
zk.enableDevice()
user = self.zkgetuser(zk)
command = CMD_ATTLOG_RRQ
command_string = ''
chksum = 0
session_id = zk.session_id
reply_id = unpack('HHHH', zk.data_recv[:8])[3]
buf = zk.createHeader(command, chksum, session_id,
reply_id, command_string)
zk.zkclient.sendto(buf, zk.address)
try:
zk.data_recv, addr = zk.zkclient.recvfrom(1024)
command = unpack('HHHH', zk.data_recv[:8])[0]
if command == CMD_PREPARE_DATA:
size = unpack('I', zk.data_recv[8:12])[0]
zk_size = size
else:
zk_size = False
if zk_size:
bytes = zk_size
while bytes > 0:
data_recv, addr = zk.zkclient.recvfrom(1032)
zk.attendancedata.append(data_recv)
bytes -= 1024
zk.session_id = unpack('HHHH', zk.data_recv[:8])[2]
data_recv = zk.zkclient.recvfrom(8)
attendance = []
if len(zk.attendancedata) > 0:
# The first 4 bytes don't seem to be related to the user
for x in xrange(len(zk.attendancedata)):
if x > 0:
zk.attendancedata[x] = zk.attendancedata[x][8:]
attendancedata = b''.join(zk.attendancedata)
attendancedata = attendancedata[14:]
while len(attendancedata) > 0:
uid, state, timestamp, space = unpack('24s1s4s11s', attendancedata.ljust(40)[:40])
pls = unpack('c', attendancedata[29:30])
uid = uid.split(b'\x00', 1)[0].decode('utf-8')
tmp = ''
for i in reversed(range(int(len(binascii.hexlify(timestamp)) / 2))):
tmp += binascii.hexlify(timestamp).decode('utf-8')[i * 2:(i * 2) + 2]
attendance.append((uid, int(binascii.hexlify(state), 16),
decode_time(int(tmp, 16)), unpack('HHHH', space[:8])[0]))
attendancedata = attendancedata[40:]
except Exception as e:
_logger.info("++++++++++++Exception++++++++++++++++++++++", e)
attendance = False
if attendance:
for each in attendance:
atten_time = each[2]
atten_time = datetime.strptime(
atten_time.strftime('%Y-%m-%d %H:%M:%S'), '%Y-%m-%d %H:%M:%S')
local_tz = pytz.timezone(
self.env.user.partner_id.tz or 'GMT')
local_dt = local_tz.localize(atten_time, is_dst=None)
utc_dt = local_dt.astimezone(pytz.utc)
utc_dt = utc_dt.strftime("%Y-%m-%d %H:%M:%S")
atten_time = datetime.strptime(
utc_dt, "%Y-%m-%d %H:%M:%S")
atten_time = fields.Datetime.to_string(atten_time)
if user:
for uid in user:
if user[uid][0] == str(each[0]):
get_user_id = self.env['hr.employee'].search(
[('device_id', '=', str(each[0]))])
if get_user_id:
duplicate_atten_ids = zk_attendance.search(
[('device_id', '=', str(each[0])), ('punching_time', '=', atten_time)])
if duplicate_atten_ids:
continue
else:
zk_attendance.create({'employee_id': get_user_id.id,
'device_id': each[0],
'attendance_type': str(each[1]),
'punch_type': str(each[3]),
'punching_time': atten_time,
'address_id': info.address_id.id})
att_var = att_obj.search([('employee_id', '=', get_user_id.id),
('check_out', '=', False)])
if each[3] == 0: #check-in
if not att_var:
att_obj.create({'employee_id': get_user_id.id,
'check_in': atten_time})
if each[3] == 1: #check-out
if len(att_var) == 1:
att_var.write({'check_out': atten_time})
else:
att_var1 = att_obj.search([('employee_id', '=', get_user_id.id)])
if att_var1:
att_var1[-1].write({'check_out': atten_time})
else:
employee = self.env['hr.employee'].create(
{'device_id': str(each[0]), 'name': user[uid][1]})
zk_attendance.create({'employee_id': employee.id,
'device_id': each[0],
'attendance_type': str(each[1]),
'punch_type': str(each[3]),
'punching_time': atten_time,
'address_id': info.address_id.id})
att_obj.create({'employee_id': employee.id,
'check_in': atten_time})
else:
pass
zk.enableDevice()
zk.disconnect()
return True
else:
raise UserError(_('Unable to get the attendance log, please try again later.'))
else:
raise UserError(_('Unable to connect, please check the parameters and network connections.'))

119
oh_hr_zk_attendance/models/zkattendance.py

@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<https://www.cybrosys.com>)
#
# This program is free software: you can modify
# it under the terms of the GNU Affero General Public License (AGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###################################################################################
import binascii
from struct import pack, unpack
from .zkconst import *
def getSizeAttendance(self):
"""Checks a returned packet to see if it returned CMD_PREPARE_DATA,
indicating that data packets are to be sent
Returns the amount of bytes that are going to be sent"""
command = unpack('HHHH', self.data_recv[:8])[0]
if command == CMD_PREPARE_DATA:
size = unpack('I', self.data_recv[8:12])[0]
return size
else:
return False
def reverseHex(hexstr):
tmp = ''
for i in reversed( xrange( int(len(hexstr)/2) ) ):
tmp += hexstr[i*2:(i*2)+2]
return tmp
def zkgetattendance(self):
"""Start a connection with the time clock"""
command = CMD_ATTLOG_RRQ
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
if getSizeAttendance(self):
bytes = getSizeAttendance(self)
while bytes > 0:
data_recv, addr = self.zkclient.recvfrom(1032)
self.attendancedata.append(data_recv)
bytes -= 1024
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
data_recv = self.zkclient.recvfrom(8)
attendance = []
if len(self.attendancedata) > 0:
# The first 4 bytes don't seem to be related to the user
for x in xrange(len(self.attendancedata)):
if x > 0:
self.attendancedata[x] = self.attendancedata[x][8:]
attendancedata = b''.join( self.attendancedata )
attendancedata = attendancedata[14:]
while len(attendancedata) > 40:
uid, state, timestamp, space = unpack( '24s1s4s11s', attendancedata.ljust(40)[:40] )
# Clean up some messy characters from the user name
#uid = unicode(uid.strip('\x00|\x01\x10x'), errors='ignore')
uid = uid.split(b'\x00', 1)[0].decode('utf-8')
#print "%s, %s, %s" % (uid, state, decode_time( int( reverseHex( timestamp.encode('hex') ), 16 ) ) )
attendance.append( ( uid, int( binascii.hexlify(state), 16 ), decode_time( int( reverseHex( binascii.hexlify(timestamp).decode('utf-8')), 16 ) ) ) )
attendancedata = attendancedata[40:]
return attendance
except:
return False
def zkclearattendance(self):
"""Start a connection with the time clock"""
command = CMD_CLEAR_ATTLOG
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

64
oh_hr_zk_attendance/models/zkconnect.py

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<https://www.cybrosys.com>)
#
# This program is free software: you can modify
# it under the terms of the GNU Affero General Public License (AGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###################################################################################
from struct import pack, unpack
from .zkconst import *
def zkconnect(self):
"""Start a connection with the time clock"""
command = CMD_CONNECT
command_string = ''
chksum = 0
session_id = 0
reply_id = -1 + USHRT_MAX
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.checkValid( self.data_recv )
except:
return False
def zkdisconnect(self):
"""Disconnect from the clock"""
command = CMD_EXIT
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
self.data_recv, addr = self.zkclient.recvfrom(1024)
return self.checkValid( self.data_recv )

71
oh_hr_zk_attendance/models/zkconst.py

@ -0,0 +1,71 @@
from datetime import datetime, date
USHRT_MAX = 65535
CMD_CONNECT = 1000
CMD_EXIT = 1001
CMD_ENABLEDEVICE = 1002
CMD_DISABLEDEVICE = 1003
CMD_ACK_OK = 2000
CMD_ACK_ERROR = 2001
CMD_ACK_DATA = 2002
CMD_PREPARE_DATA = 1500
CMD_DATA = 1501
CMD_USERTEMP_RRQ = 9
CMD_ATTLOG_RRQ = 13
CMD_CLEAR_DATA = 14
CMD_CLEAR_ATTLOG = 15
CMD_WRITE_LCD = 66
CMD_GET_TIME = 201
CMD_SET_TIME = 202
CMD_VERSION = 1100
CMD_DEVICE = 11
CMD_CLEAR_ADMIN = 20
CMD_SET_USER = 8
LEVEL_USER = 0
LEVEL_ADMIN = 14
def encode_time(t):
"""Encode a timestamp send at the timeclock
copied from zkemsdk.c - EncodeTime"""
d = ( (t.year % 100) * 12 * 31 + ((t.month - 1) * 31) + t.day - 1) *\
(24 * 60 * 60) + (t.hour * 60 + t.minute) * 60 + t.second
return d
def decode_time(t):
"""Decode a timestamp retrieved from the timeclock
copied from zkemsdk.c - DecodeTime"""
second = t % 60
t = t / 60
minute = t % 60
t = t / 60
hour = t % 24
t = t / 24
day = t % 31+1
t = t / 31
month = t % 12+1
t = t / 12
year = t + 2000
d = datetime(int(year), int(month), int(day), int(hour), int(minute), int(second))
return d

82
oh_hr_zk_attendance/models/zkdevice.py

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<https://www.cybrosys.com>)
#
# This program is free software: you can modify
# it under the terms of the GNU Affero General Public License (AGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###################################################################################
from struct import pack, unpack
from .zkconst import *
def zkdevicename(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~DeviceName'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkenabledevice(self):
"""Start a connection with the time clock"""
command = CMD_ENABLEDEVICE
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkdisabledevice(self):
"""Start a connection with the time clock"""
command = CMD_DISABLEDEVICE
command_string = '\x00\x00'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

56
oh_hr_zk_attendance/models/zkextendfmt.py

@ -0,0 +1,56 @@
def zkextendfmt(self):
try:
test = self.exttrynumber
except:
self.exttrynumber = 1
data_seq=[ self.data_recv.encode("hex")[4:6], self.data_recv.encode("hex")[6:8] ]
#print data_seq
if self.exttrynumber == 1:
plus1 = 0
plus2 = 0
else:
plus1 = -1
plus2 = +1
desc = ": +"+hex( int('99', 16)+plus1 ).lstrip('0x')+", +"+hex(int('b1', 16)+plus2).lstrip("0x")
self.data_seq1 = hex( int( data_seq[0], 16 ) + int( '99', 16 ) + plus1 ).lstrip("0x")
self.data_seq2 = hex( int( data_seq[1], 16 ) + int( 'b1', 16 ) + plus2 ).lstrip("0x")
if len(self.data_seq1) >= 3:
#self.data_seq2 = hex( int( self.data_seq2, 16 ) + int( self.data_seq1[:1], 16) ).lstrip("0x")
self.data_seq1 = self.data_seq1[-2:]
if len(self.data_seq2) >= 3:
#self.data_seq1 = hex( int( self.data_seq1, 16 ) + int( self.data_seq2[:1], 16) ).lstrip("0x")
self.data_seq2 = self.data_seq2[-2:]
if len(self.data_seq1) <= 1:
self.data_seq1 = "0"+self.data_seq1
if len(self.data_seq2) <= 1:
self.data_seq2 = "0"+self.data_seq2
counter = hex( self.counter ).lstrip("0x")
if len(counter):
counter = "0" + counter
#print self.data_seq1+" "+self.data_seq2+desc
data = "0b00"+self.data_seq1+self.data_seq2+self.id_com+counter+"007e457874656e64466d7400"
self.zkclient.sendto(data.decode("hex"), self.address)
#print data
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
except:
if self.exttrynumber == 1:
self.exttrynumber = 2
tmp = zkextendfmt(self)
if len(tmp) < 1:
self.exttrynumber = 1
self.id_com = self.data_recv.encode("hex")[8:12]
self.counter = self.counter+1
#print self.data_recv.encode("hex")
return self.data_recv[8:]

62
oh_hr_zk_attendance/models/zkextendoplog.py

@ -0,0 +1,62 @@
def zkextendoplog(self, index=0):
try:
test = self.extlogtrynumber
except:
self.extlogtrynumber = 1
data_seq = [ self.data_recv.encode("hex")[4:6], self.data_recv.encode("hex")[6:8] ]
#print data_seq
if index==0:
self.data_seq1 = hex( int( data_seq[0], 16 ) + int( '104', 16 ) ).lstrip("0x")
self.data_seq2 = hex( int( data_seq[1], 16 ) + int( '19', 16 ) ).lstrip("0x")
desc = ": +104, +19"
header="0b00"
elif index==1:
self.data_seq1 = hex( abs( int( data_seq[0], 16 ) - int( '2c', 16 ) ) ).lstrip("0x")
self.data_seq2 = hex( abs( int( data_seq[1], 16 ) - int( '2', 16 ) ) ).lstrip("0x")
desc = ": -2c, -2"
header="d107"
elif index>=2:
self.data_seq1 = hex( abs( int( data_seq[0], 16 ) - int( '2c', 16 ) ) ).lstrip("0x")
self.data_seq2 = hex( abs( int( data_seq[1], 16 ) - int( '2', 16 ) ) ).lstrip("0x")
desc = ": -2c, -2"
header="ffff"
#print self.data_seq1+" "+self.data_seq2
if len(self.data_seq1) >= 3:
self.data_seq2 = hex( int( self.data_seq2, 16 ) + int( self.data_seq1[:1], 16) ).lstrip("0x")
self.data_seq1 = self.data_seq1[-2:]
if len(self.data_seq2) >= 3:
self.data_seq1 = hex( int( self.data_seq1, 16 ) + int( self.data_seq2[:1], 16) ).lstrip("0x")
self.data_seq2 = self.data_seq2[-2:]
if len(self.data_seq1) <= 1:
self.data_seq1 = "0"+self.data_seq1
if len(self.data_seq2) <= 1:
self.data_seq2 = "0"+self.data_seq2
counter = hex( self.counter ).lstrip("0x")
if len(counter):
counter = "0" + counter
#print self.data_seq1+" "+self.data_seq2+desc
data = header+self.data_seq1+self.data_seq2+self.id_com+counter+"00457874656e644f504c6f6700"
self.zkclient.sendto(data.decode("hex"), self.address)
#print data
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
except:
bingung=1
if self.extlogtrynumber == 1:
self.extlogtrynumber = 2
zkextendoplog(self)
self.id_com = self.data_recv.encode("hex")[8:12]
self.counter = self.counter+1
#print self.data_recv.encode("hex")
return self.data_recv[8:]

44
oh_hr_zk_attendance/models/zkface.py

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<https://www.cybrosys.com>)
#
# This program is free software: you can modify
# it under the terms of the GNU Affero General Public License (AGPL) as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###################################################################################
from struct import pack, unpack
from .zkconst import *
def zkfaceon(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = 'FaceFunOn'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

156
oh_hr_zk_attendance/models/zklib.py

@ -0,0 +1,156 @@
from socket import *
from .zkconnect import *
from .zkversion import *
from .zkos import *
from .zkextendfmt import *
from .zkextendoplog import *
from .zkplatform import *
from .zkworkcode import *
from .zkssr import *
from .zkpin import *
from .zkface import *
from .zkserialnumber import *
from .zkdevice import *
from .zkuser import *
from .zkattendance import *
from .zktime import *
class ZKLib:
def __init__(self, ip, port):
self.address = (ip, port)
self.zkclient = socket(AF_INET, SOCK_DGRAM)
self.zkclient.settimeout(3)
self.session_id = 0
self.userdata = []
self.attendancedata = []
def createChkSum(self, p):
"""This function calculates the chksum of the packet to be sent to the
time clock
Copied from zkemsdk.c"""
l = len(p)
chksum = 0
while l > 1:
chksum += unpack('H', pack('BB', p[0], p[1]))[0]
p = p[2:]
if chksum > USHRT_MAX:
chksum -= USHRT_MAX
l -= 2
if l:
chksum = chksum + p[-1]
while chksum > USHRT_MAX:
chksum -= USHRT_MAX
chksum = ~chksum
while chksum < 0:
chksum += USHRT_MAX
return pack('H', chksum)
def createHeader(self, command, chksum, session_id, reply_id,
command_string):
"""This function puts a the parts that make up a packet together and
packs them into a byte string"""
buf = pack('HHHH', command, chksum, session_id, reply_id) + command_string.encode(encoding='utf_8', errors='strict')
buf = unpack('8B'+'%sB' % len(command_string), buf)
chksum = unpack('H', self.createChkSum(buf))[0]
#print unpack('H', self.createChkSum(buf))
reply_id += 1
if reply_id >= USHRT_MAX:
reply_id -= USHRT_MAX
buf = pack('HHHH', command, chksum, session_id, reply_id)
return buf + command_string.encode(encoding='utf_8', errors='strict')
def checkValid(self, reply):
"""Checks a returned packet to see if it returned CMD_ACK_OK,
indicating success"""
command = unpack('HHHH', reply[:8])[0]
if command == CMD_ACK_OK:
return True
else:
return False
def connect(self):
return zkconnect(self)
def disconnect(self):
return zkdisconnect(self)
def version(self):
return zkversion(self)
def osversion(self):
return zkos(self)
def extendFormat(self):
return zkextendfmt(self)
def extendOPLog(self, index=0):
return zkextendoplog(self, index)
def platform(self):
return zkplatform(self)
def fmVersion(self):
return zkplatformVersion(self)
def workCode(self):
return zkworkcode(self)
def ssr(self):
return zkssr(self)
def pinWidth(self):
return zkpinwidth(self)
def faceFunctionOn(self):
return zkfaceon(self)
def serialNumber(self):
return zkserialnumber(self)
def deviceName(self):
return zkdevicename(self)
def disableDevice(self):
return zkdisabledevice(self)
def enableDevice(self):
return zkenabledevice(self)
def getUser(self):
return zkgetuser(self)
def setUser(self, uid, userid, name, password, role):
return zksetuser(self, uid, userid, name, password, role)
def clearUser(self):
return zkclearuser(self)
def clearAdmin(self):
return zkclearadmin(self)
def getAttendance(self):
return zkgetattendance(self)
def clearAttendance(self):
return zkclearattendance(self)
def setTime(self, t):
return zksettime(self, t)
def getTime(self):
return zkgettime(self)

23
oh_hr_zk_attendance/models/zkos.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkos(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~OS'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

23
oh_hr_zk_attendance/models/zkpin.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkpinwidth(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~PIN2Width'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

43
oh_hr_zk_attendance/models/zkplatform.py

@ -0,0 +1,43 @@
from struct import pack, unpack
from .zkconst import *
def zkplatform(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~Platform'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkplatformVersion(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~ZKFPVersion'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

23
oh_hr_zk_attendance/models/zkserialnumber.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkserialnumber(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~SerialNumber'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

23
oh_hr_zk_attendance/models/zkssr.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkssr(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~SSR'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

50
oh_hr_zk_attendance/models/zktime.py

@ -0,0 +1,50 @@
from struct import pack, unpack
from .zkconst import *
def reverseHex(hexstr):
tmp = ''
for i in reversed( xrange( len(hexstr)/2 ) ):
tmp += hexstr[i*2:(i*2)+2]
return tmp
def zksettime(self, t):
"""Start a connection with the time clock"""
command = CMD_SET_TIME
command_string = pack('I',encode_time(t))
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkgettime(self):
"""Start a connection with the time clock"""
command = CMD_GET_TIME
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return decode_time( int( reverseHex( self.data_recv[8:].encode("hex") ), 16 ) )
except:
return False

140
oh_hr_zk_attendance/models/zkuser.py

@ -0,0 +1,140 @@
from struct import pack, unpack
from .zkconst import *
def getSizeUser(self):
"""Checks a returned packet to see if it returned CMD_PREPARE_DATA,
indicating that data packets are to be sent
Returns the amount of bytes that are going to be sent"""
command = unpack('HHHH', self.data_recv[:8])[0]
if command == CMD_PREPARE_DATA:
size = unpack('I', self.data_recv[8:12])[0]
return size
else:
return False
def zksetuser(self, uid, userid, name, password, role):
"""Start a connection with the time clock"""
command = CMD_SET_USER
command_string = pack('sxs8s28ss7sx8s16s', chr( uid ), chr(role), password, name, chr(1), '', userid, '' )
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkgetuser(self):
"""Start a connection with the time clock"""
command = CMD_USERTEMP_RRQ
command_string = '\x05'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
if getSizeUser(self):
bytes = getSizeUser(self)
while bytes > 0:
data_recv, addr = self.zkclient.recvfrom(1032)
self.userdata.append(data_recv)
bytes -= 1024
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
data_recv = self.zkclient.recvfrom(8)
users = {}
if len(self.userdata) > 0:
# The first 4 bytes don't seem to be related to the user
for x in xrange(len(self.userdata)):
if x > 0:
self.userdata[x] = self.userdata[x][8:]
userdata = ''.join( self.userdata )
userdata = userdata[11:]
while len(userdata) > 72:
uid, role, password, name, userid = unpack( '2s2s8s28sx31s', userdata.ljust(72)[:72] )
uid = int( uid.encode("hex"), 16)
# Clean up some messy characters from the user name
password = password.split('\x00', 1)[0]
password = unicode(password.strip('\x00|\x01\x10x'), errors='ignore')
#uid = uid.split('\x00', 1)[0]
userid = unicode(userid.strip('\x00|\x01\x10x'), errors='ignore')
name = name.split('\x00', 1)[0]
if name.strip() == "":
name = uid
users[uid] = (userid, name, int( role.encode("hex"), 16 ), password)
#print("%d, %s, %s, %s, %s" % (uid, userid, name, int( role.encode("hex"), 16 ), password))
userdata = userdata[72:]
return users
except:
return False
def zkclearuser(self):
"""Start a connection with the time clock"""
command = CMD_CLEAR_DATA
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkclearadmin(self):
"""Start a connection with the time clock"""
command = CMD_CLEAR_ADMIN
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

23
oh_hr_zk_attendance/models/zkversion.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkversion(self):
"""Start a connection with the time clock"""
command = CMD_VERSION
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

23
oh_hr_zk_attendance/models/zkworkcode.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkworkcode(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = 'WorkCode'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

4
oh_hr_zk_attendance/security/ir.model.access.csv

@ -0,0 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hr_zk_machine_user,zk.machine.hr_biometric_machine,model_zk_machine,hr_attendance.group_hr_attendance_user,1,1,1,1
access_hr_zk_machine_user1,zk.machine.hr_biometric_machine1,model_zk_machine_attendance,hr_attendance.group_hr_attendance_user,1,1,1,1
access_hr_zk_machine_user2,zk.machine.hr_biometric_machine2,model_zk_report_daily_attendance,hr_attendance.group_hr_attendance_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_hr_zk_machine_user zk.machine.hr_biometric_machine model_zk_machine hr_attendance.group_hr_attendance_user 1 1 1 1
3 access_hr_zk_machine_user1 zk.machine.hr_biometric_machine1 model_zk_machine_attendance hr_attendance.group_hr_attendance_user 1 1 1 1
4 access_hr_zk_machine_user2 zk.machine.hr_biometric_machine2 model_zk_report_daily_attendance hr_attendance.group_hr_attendance_user 1 1 1 1

BIN
oh_hr_zk_attendance/static/description/attendance_log.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
oh_hr_zk_attendance/static/description/banner.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 KiB

BIN
oh_hr_zk_attendance/static/description/cybro_logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
oh_hr_zk_attendance/static/description/devicelog.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
oh_hr_zk_attendance/static/description/employee.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
oh_hr_zk_attendance/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

164
oh_hr_zk_attendance/static/description/index.html

@ -0,0 +1,164 @@
<section class="oe_container">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan">HR Biometric Device Integration</h2>
<h3 class="oe_slogan">This Module Integrates Biometric Device With HR Attendance</h3>
<h4 class="oe_slogan"><a href="https://www.cybrosys.com">Cybrosys Technologies</a> </h4>
</div>
<div class="oe_row oe_spaced" style="padding-left:65px;">
<div>
<span style="color:green;"> &#9745; </span> Ingrates biometric device(Face+Thumb) with HR attendance.<br/>
<span style="color:green;"> &#9745; </span> Option to keep the device attendance log in Odoo.<br/>
<span style="color:green;"> &#9745; </span> Option to clear the device attendance log from both device and Odoo.<br/>
<span style="color:green;"> &#9745; </span> Automating HR attendance.<br/>
<span style="color:green;"> &#9745; </span> Option to configure multiple devices.<br/>
</div>
<br/><br/>
<div>
<span align="center" class="fa fa-star fa-spin"></span>
<b><i>This module will support with <br/><t/>
<ul style="padding-left: 36px;padding-top: 2px;">
<li style="list-style:outside !important;">ZKteco model 'uFace 202'</li>
<li style="list-style:outside !important;">ZKteco model 'iFace990'</li>
</ul>
</i></b>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div class="oe_picture">
<h3 class="oe_slogan">Overview</h3>
<p class="oe_mt32 text-justify" style="text-align: center;">
Automation is an implementation factor for a successful ERP. With this module,
HR attendance can automate by integrating Thumb / Face detection device with Odoo.
We can configure a user both from thumbing device or Odoo employee form.
</p>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div style="text-align: center">
<p>
<h4>Biometric Device Configuration</h4>
<p>
</div>
<div style="text-align: center">
<span>Here you can configure your all devices with it's IP address and port number.</span>
<div class="oe_demo oe_picture oe_screenshot">
<img style="border:10px solid white;" src="zk_config.png">
</div>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div style="text-align: center">
<p>
<h4>Download/Clear Device Attendance Log</h4>
<p>
</div>
<div style="text-align: center">
<span>After configuration, you can download your device attendance log into Odoo through
'Download' button.If the device is connected, then the Odoo will download all device
attendance log.
Otherwise, the Odoo will show you a warning message as follow.</span>
<div class="oe_demo oe_picture oe_screenshot">
<img style="border:10px solid white;" src="zk_warning.png">
</div>
</div>
<div style="text-align: center">
<span>You can also clear all attendance log from both Odoo and device via 'Clear' button.
If the device is not connected it will show you a warning message as follow.</span>
<div class="oe_demo oe_picture oe_screenshot">
<img style="border:10px solid white;" src="zk_warn2.png">
</div>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div style="text-align: center">
<p>
<h4>Biometric Device Attendance Log</h4>
<p>
</div>
<div style="text-align: center">
<span>Here you can see all device attendance log</span>
<div class="oe_demo oe_picture oe_screenshot">
<img style="border:10px solid white;" src="devicelog.png">
</div>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div style="text-align: center">
<p>
<h4>HR Attendance</h4>
<p>
</div>
<div style="text-align: center">
<span>Here, Odoo automatically generates HR attendance log while downloading the device attendance.</span>
<div class="oe_demo oe_picture oe_screenshot">
<img style="border:10px solid white;" src="attendance_log.png">
</div>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div style="text-align: center">
<p>
<h4>Employee Configuration</h4>
<p>
</div>
<div style="text-align: center">
<span>You can update existing employees with the 'Device Id' which are the id in the biometric device.
If there is no match with the biometric device id then system will automatically create corresponding employee.</span>
<div class="oe_demo oe_picture oe_screenshot">
<img style="border:10px solid white;" src="employee.png">
</div>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="" style="text-align: center">
<span><b><i>Note:- This integration is only applicable for the the device ZKteco model 'uFace 202'<br/>
Please install zklib library (sudo pip install zklib)</i></b></span>
</div>
</section>
<section class="oe_container">
<h2 class="oe_slogan" style="margin-top:20px;" >Need Any Help?</h2>
<div class="oe_slogan" style="margin-top:10px !important;">
<div>
<a class="btn btn-primary btn-lg mt8"
style="color: #FFFFFF !important;border-radius: 0;" href="https://www.cybrosys.com"><i
class="fa fa-envelope"></i> Email </a> <a
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;"
href="https://www.cybrosys.com/contact/"><i
class="fa fa-phone"></i> Contact Us </a> <a
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;"
href="https://www.cybrosys.com/odoo-customization-and-installation/"><i
class="fa fa-check-square"></i> Request Customization </a>
</div>
<br>
<img src="cybro_logo.png" style="width: 190px; margin-bottom: 20px;" class="center-block">
<div>
<a href="https://twitter.com/cybrosys" target="_blank"><i class="fa fa-2x fa-twitter" style="color:white;background: #00a0d1;width:35px;"></i></a></td>
<a href="https://www.linkedin.com/company/cybrosys-technologies-pvt-ltd" target="_blank"><i class="fa fa-2x fa-linkedin" style="color:white;background: #31a3d6;width:35px;padding-left: 3px;"></i></a></td>
<a href="https://www.facebook.com/cybrosystechnologies" target="_blank"><i class="fa fa-2x fa-facebook" style="color:white;background: #3b5998;width:35px;padding-left: 8px;"></i></a></td>
<a href="https://plus.google.com/106641282743045431892/about" target="_blank"><i class="fa fa-2x fa-google-plus" style="color:white;background: #c53c2c;width:35px;padding-left: 3px;"></i></a></td>
<a href="https://in.pinterest.com/cybrosys" target="_blank"><i class="fa fa-2x fa-pinterest" style="color:white;background: #ac0f18;width:35px;padding-left: 3px;"></i></a></td>
</div>
</div>
</section>

BIN
oh_hr_zk_attendance/static/description/zk_config.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
oh_hr_zk_attendance/static/description/zk_warn2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
oh_hr_zk_attendance/static/description/zk_warning.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

71
oh_hr_zk_attendance/views/zk_machine_attendance_view.xml

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Customize Attendance Report -->
<record id="inherited_view_attendance_tree" model="ir.ui.view">
<field name="name">inherited_hr.attendance.tree</field>
<field name="model">hr.attendance</field>
<field name="inherit_id" ref="hr_attendance.view_attendance_tree"/>
<field name="arch" type="xml">
<tree string="Employee attendances">
<field name="worked_hours" type="measure"/>
</tree>
</field>
</record>
<record id="inherited_hr_attendance_view_filter" model="ir.ui.view">
<field name="name">inherited_hr_attendance_view_filter</field>
<field name="model">hr.attendance</field>
<field name="inherit_id" ref="hr_attendance.hr_attendance_view_filter"/>
<field name="arch" type="xml">
<search string="Hr Attendance Search">
<group expand="0" string="Group By">
<separator/>
<filter name="date" string="Date" context="{'group_by':'check_in:day'}"/>
</group>
</search>
</field>
</record>
<record id="view_zk_report_daily_attendance_search" model="ir.ui.view">
<field name="name">zk.report.daily.attendance.search</field>
<field name="model">zk.report.daily.attendance</field>
<field name="arch" type="xml" >
<search string="Hr Attendance Search">
<filter icon="terp-stock_align_left_24" string="My Attendance" name="my_attendance" domain="[('name.user_id.id', '=', uid)]" />
<filter name="today" string="Today" domain="[('punching_time', '&gt;=', datetime.datetime.now().replace(hour=0, minute=0, second=0)),('punching_time', '&lt;=', datetime.datetime.now().replace(hour=23, minute=59, second=59))]" />
<filter string="Current Month" domain="[('punching_time', '&gt;=', datetime.datetime.now().strftime('%Y-%m-01'))]" />
<separator/>
<field name="name"/>
</search>
</field>
</record>
<record id="view_zk_report_daily_attendance_tree" model="ir.ui.view">
<field name="name">zk.report.daily.attendance.tree</field>
<field name="model">zk.report.daily.attendance</field>
<field name="arch" type="xml" >
<tree string="Attendance" create="false" delete="false" colors="green:punch_type in ('0');red:punch_type in ('1');">
<field name="punching_day"/>
<field name="name"/>
<field name="punch_type"/>
<field name="attendance_type"/>
<field name="punching_time"/>
<field name="address_id"/>
</tree>
</field>
</record>
<record id="action_zk_report_daily_attendance" model="ir.actions.act_window">
<field name="name">Attendance Analysis</field>
<field name="res_model">zk.report.daily.attendance</field>
<field name="view_type">form</field>
<field name="view_mode">tree</field>
<field name="context">{'search_default_my_attendance':1}</field>
<field name="search_view_id" ref="view_zk_report_daily_attendance_search" />
</record>
<menuitem id="menu_zk_attendance_view" name="Attendance log" action="action_zk_report_daily_attendance" parent="zk_machine_menu"
sequence="2" groups="hr_attendance.group_hr_attendance_user"/>
</odoo>

69
oh_hr_zk_attendance/views/zk_machine_view.xml

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_zk_machine_form" model="ir.ui.view">
<field name="name">zk.machine.form</field>
<field name="model">zk.machine</field>
<field name="arch" type="xml">
<form string="Biometric Device">
<header>
<button name="clear_attendance" type="object" string="Clear Data" class="oe_highlight"
icon="fa-remove " confirm="Are you sure you want to do this?"/>
<button name="download_attendance" type="object" string="Download Data" class="oe_highlight"
icon="fa-download " confirm="Are you sure you want to do this?" />
</header>
<sheet>
<div class="oe_title">
<label for="name" />
<h1>
<field name="name" placeholder="Machine IP"/>
</h1>
</div>
<group>
<group>
<field name="port_no"/>
<field name="address_id"/>
</group>
<group>
<field name="company_id" groups="base.group_multi_company"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_zk_machine_tree" model="ir.ui.view">
<field name="name">zk.machine.tree</field>
<field name="model">zk.machine</field>
<field name="arch" type="xml">
<tree string="Biometric Machine">
<field name="name"/>
<field name="port_no"/>
<field name="address_id"/>
<field name="company_id" groups="base.group_multi_company"/>
</tree>
</field>
</record>
<record id="zk_machine_action" model="ir.actions.act_window">
<field name="name">Attendances</field>
<field name="res_model">zk.machine</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<record id="hr_employee_inherit_form_view" model="ir.ui.view">
<field name="name">hr.employee.form</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<field name="user_id" position="after">
<field name="device_id"/>
</field>
</field>
</record>
<menuitem id="zk_machine_menu" parent="hr_attendance.menu_hr_attendance_root" sequence="50" name="Biometric Manager" />
<menuitem id="zk_machine_sub_menu" parent="zk_machine_menu" name="Device Configuration" action="zk_machine_action" sequence="1"/>
</odoo>
Loading…
Cancel
Save