#!/usr/bin/python3 import configparser import ruamel.yaml import sys import json import re import os import time SYSTEM_CONF_PATH=["/etc/speaker.conf", "/oem/etc/volctrl.conf", "/oem/etc/schedule.conf", "/oem/.record/firmware_url"] TMPFILE="/tmp/conf" origin_keys = { "system": [ "firmware", "hard_version", "model", "ui_model" ], "account_info_p2p": [ "transport", "allowanoymous" ], "secret": [ "ui_username" ], "upgrade": [ "reset_default", "date" ], "volume": [ "hard_volume_control" ] } comment_data = { 'system': { 'system': { 'cn': '系统设置.', 'en': 'System Settings.' }, 'language': { 'cn': '系统语音语言. 值:cn | en', 'en': 'System voice language. Value: cn | en', 'check': { 'type': 'string', 'empty': False, 'method': 'enums', 'data_enums': ['cn', 'en'] } }, 'broadcast_service': { 'cn': 'IP Audio Center服务开启. 值:yes | no', 'en': 'Enable IP Audio Center Service. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'account_info_1': [ 'server', 'port', 'username', 'authuser', 'reg_expires', 'transport', 'enable' ] } } } }, 'ip_assign': { 'cn': '系统IP获取方式. 值:static | dhcp', 'en': 'System IP get mode. Value: static | dhcp', 'check': { 'type': 'string', 'empty': False, 'relate': True, 'data_relate': { 'static': { 'system': [ 'ipaddr', 'netmask', 'gateway', 'dns1' ] } }, 'method': 'enums', 'data_enums': ['static', 'dhcp'] } }, 'ipaddr': { 'cn': '系统IP地址. 例: 192.168.1.100', 'en': 'System IP address. el: 192.168.1.100', 'check': { 'type': 'string', 'method': 'regex_ip' } }, 'netmask': { 'cn': '子网掩码. 例:255.255.255.0', 'en': 'Subnet mask. el: 255.255.255.0', 'check': { 'type': 'string', 'method': 'regex_ip' } }, 'gateway': { 'cn': '系统网络网关地址 例:192.168.1.1', 'en': 'System gateway ip address. el: 192.168.1.1', 'check': { 'type': 'string', 'method': 'regex_ip' } }, 'dns1': { 'cn': '系统主dns地址. 例: 114.114.114.114', 'en': 'System Primary DNS address. el: 114.114.114.114', 'check': { 'type': 'string', 'method': 'regex_ip' } }, 'dns2': { 'cn': '系统备dns地址. 例: 8.8.8.8', 'en': 'System Alternative DNS address. el: 8.8.8.8', 'check': { 'type': 'string', 'method': 'regex_ip' } }, 'enable_ntp': { 'cn': '启动NTP服务(系统自动获取网络时间) 值:yes | no', 'en': 'Enanle NTP service(Auto get system time from network). Vlaue: yes | no', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'system': [ 'ntpserver', 'timezone', 'tzname' ] } } } }, 'ntpserver': { 'cn': 'NTP服务器地址. 例: pool.ntp.org', 'en': 'NTP server address. el: pool.ntp.org', 'check': { 'type': 'string', 'method': 'regex_domain' } }, 'timezone': { 'cn': '系统时区 例: CST-8', 'en': 'System time zone. el: CST-8', 'check': { 'type': 'string', 'method': 'default' } }, 'tzname': { 'cn': '系统时区名 例: Asia/Chongqing', 'en': 'System time zone name. el: Asia/chongqing', 'check': { 'type': 'string', 'method': 'default' } }, 'location': { 'cn': '系统备注信息 例: Confrence intercom', 'en': 'System mark information. el: Confrence intercom', 'check': { 'type': 'string', 'method': 'default' } }, 'access_type': { 'cn': 'WEB UI访问协议 值:0 | 1 | 2. 例: 1 (http)', 'en': 'WEB UI access protocol. Value: 0 | 1 | 2. el: 1 (http)', 'check': { 'type': 'string', 'empty': False, 'method': 'enums', 'data_enums': ['0', '1', '2'] } }, 'route': { 'cn': '系统默认路由 值:local | pppoe. 例: local', 'en': 'System default route. Value: local | pppoe. el: pppoe', 'check': { 'type': 'string', 'empty': False, 'method': 'enums', 'data_enums': ['local', 'pppoe'] } } }, 'account_info_1': { 'account_info_1': { 'cn': '主SIP账号配置.', 'en': 'Primary SIP Account Settings.' }, 'server': { 'cn': 'SIP服务器地址.', 'en': 'SIP server address.', 'check': { 'type': 'string', 'method': 'regex_domain' } }, 'port': { 'cn': 'SIP服务器端口. 范围: 1 - 65535. 例: 5060', 'en': 'System server port. range: 1 - 65535. el: 5060', 'check': { 'type': 'int', 'method': 'range', 'range': { 'start': 1, 'end': 65535 } } }, 'username': { 'cn': 'SIP用户名.', 'en': 'SIP username.', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'authuser': { 'cn': 'SIP认证用户.', 'en': 'SIP Auth user.', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'passwd': { 'cn': 'SIP账号密码.', 'en': 'SIP account password.', 'check': { 'type': 'string', 'method': 'default' } }, 'enable': { 'cn': '启用此SIP账号. 值: yes | no', 'en': 'Enable SIP account. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'account_info_1': [ 'server', 'port', 'username', 'authuser', 'reg_expires', 'transport' ] } } } }, 'reg_expires': { 'cn': '注册过期时间(秒). 范围: 1 - 3600. 例: 180', 'en': 'Register expires(Seconds). range: 1 - 3600. el: 180', 'check': { 'type': 'int', 'method': 'range', 'range': { 'start': 1, 'end': 3600 } } }, 'transport': { 'cn': '网络传输协议. 值: udp | tcp | tls', 'en': 'Network transmission protocol. Value: udp | tcp | tls', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': ['udp', 'tcp', 'tls'] } }, 'autoanswer': { 'cn': '自动接听. 值:yes | no | 10 (延迟10秒应答)', 'en': 'Auto Answer. Value: yes | no | 10 (delay 10 Seconds answer)', 'check': { 'type': 'string', 'empty': False, 'relate': True, 'data_relate': { 'yes': {}, 'no': { 'account_info_1': [ 'ringfile' ] }, 'default': { 'account_info_1': [ 'ringfile' ] } }, 'method': 'default' } }, 'ringfile': { 'cn': '来电铃声. 例: sweet.wav', 'en': 'Ring. el: sweet.wav', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'alarm_tone0.wav', 'alarm_tone1.wav', 'alarm_tone2.wav', 'bell_burglar_ring.wav', 'bells_music_tone.wav', 'bigben.wav', 'digital_telephone.wav', 'emergency_alarm_tone.wav', 'emergency_beep_tone.wav', 'oldphone.wav', 'ring.wav', 'special_music_tone.wav', 'sweet.wav', 'toy.wav', '1.wav', '2.wav', '3.wav', '4.wav', '5.wav' ] } }, 'nat_mode': { 'cn': 'NAT模式. 值: disabled | stun | turn | ice', 'en': 'NAT Mode. Value: disabled | stun | turn | ice', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'disabled', 'stun', 'turn', 'ice' ] } }, 'stun_server': { 'cn': '服务器地址.', 'en': 'Server address.', 'check': { 'type': 'string', 'method': 'regex_domain' } }, 'stun_port': { 'cn': '服务器端口. 范围: 1 - 65535. 例: 3478', 'en': 'Server port. range: 1 - 65535. el: 3478', 'check': { 'type': 'int', 'method': 'range', 'range': { 'start': 1, 'end': 65535 } } }, 'nat_username': { 'cn': 'NAT用户名.', 'en': 'NAT username.', 'check': { 'type': 'string', 'method': 'default' } }, 'nat_password': { 'cn': 'NAT账号密码.', 'en': 'NAT account password.', 'check': { 'type': 'string', 'method': 'default' } } }, 'account_info_2': { 'account_info_2': { 'cn': '备SIP账号1配置.', 'en': 'Secondary SIP Account-1 Settings.' }, 'server': { 'cn': 'SIP服务器地址.', 'en': 'SIP server address.', 'check': { 'type': 'string', 'method': 'regex_domain' } }, 'port': { 'cn': 'SIP服务器端口. 范围: 1 - 65535. 例: 5060', 'en': 'System server port. range: 1 - 65535. el: 5060', 'check': { 'type': 'int', 'method': 'range', 'range': { 'start': 1, 'end': 65535 } } }, 'username': { 'cn': 'SIP用户名.', 'en': 'SIP username.', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'authuser': { 'cn': 'SIP认证用户.', 'en': 'SIP Auth user.', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'passwd': { 'cn': 'SIP账号密码.', 'en': 'SIP account password.', 'check': { 'type': 'string', 'method': 'default' } }, 'enable': { 'cn': '启用此SIP账号. 值: yes | no', 'en': 'Enable SIP account. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'account_info_2': [ 'server', 'port', 'username', 'authuser', 'reg_expires', 'transport' ] } } } }, 'reg_expires': { 'cn': '注册过期时间(秒). 范围: 1 - 3600. 例: 180', 'en': 'Register expires(Seconds). range: 1 - 3600. el: 180', 'check': { 'type': 'int', 'method': 'range', 'range': { 'start': 1, 'end': 3600 } } }, 'transport': { 'cn': '网络传输协议. 值: udp | tcp | tls', 'en': 'Network transmission protocol. Value: udp | tcp | tls', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': ['udp', 'tcp', 'tls'] } }, 'autoanswer': { 'cn': '自动接听. 值:yes | no | 10 (延迟10秒应答)', 'en': 'Auto Answer. Value: yes | no | 10 (delay 10 Seconds answer)', 'check': { 'type': 'string', 'empty': False, 'relate': True, 'data_relate': { 'yes': {}, 'no': { 'account_info_2': [ 'ringfile' ] }, 'default': { 'account_info_2': [ 'ringfile' ] } }, 'method': 'default' } }, 'ringfile': { 'cn': '来电铃声. 例: sweet.wav', 'en': 'Ring. el: sweet.wav', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'alarm_tone0.wav', 'alarm_tone1.wav', 'alarm_tone2.wav', 'bell_burglar_ring.wav', 'bells_music_tone.wav', 'bigben.wav', 'digital_telephone.wav', 'emergency_alarm_tone.wav', 'emergency_beep_tone.wav', 'oldphone.wav', 'ring.wav', 'special_music_tone.wav', 'sweet.wav', 'toy.wav', '1.wav', '2.wav', '3.wav', '4.wav', '5.wav' ] } }, 'nat_mode': { 'cn': 'NAT模式. 值: disabled | stun | turn | ice', 'en': 'NAT Mode. Value: disabled | stun | turn | ice', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'disabled', 'stun', 'turn', 'ice' ] } }, 'stun_server': { 'cn': '服务器地址.', 'en': 'Server address.', 'check': { 'type': 'string', 'method': 'regex_domain' } }, 'stun_port': { 'cn': '服务器端口. 范围: 1 - 65535. 例: 3478', 'en': 'Server port. range: 1 - 65535. el: 3478', 'check': { 'type': 'int', 'method': 'range', 'range': { 'start': 1, 'end': 65535 } } }, 'nat_username': { 'cn': 'NAT用户名.', 'en': 'NAT username.', 'check': { 'type': 'string', 'method': 'default' } }, 'nat_password': { 'cn': 'NAT账号密码.', 'en': 'NAT account password.', 'check': { 'type': 'string', 'method': 'default' } } }, 'account_info_3': { 'account_info_3': { 'cn': '备SIP账号2配置.', 'en': 'Secondary SIP Account-2 Settings.' }, 'server': { 'cn': 'SIP服务器地址.', 'en': 'SIP server address.', 'check': { 'type': 'string', 'method': 'regex_domain' } }, 'port': { 'cn': 'SIP服务器端口. 范围: 1 - 65535. 例: 5060', 'en': 'System server port. range: 1 - 65535. el: 5060', 'check': { 'type': 'int', 'method': 'range', 'range': { 'start': 1, 'end': 65535 } } }, 'username': { 'cn': 'SIP用户名.', 'en': 'SIP username.', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'authuser': { 'cn': 'SIP认证用户.', 'en': 'SIP Auth user.', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'passwd': { 'cn': 'SIP账号密码.', 'en': 'SIP account password.', 'check': { 'type': 'string', 'method': 'default' } }, 'enable': { 'cn': '启用此SIP账号. 值: yes | no', 'en': 'Enable SIP account. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'account_info_3': [ 'server', 'port', 'username', 'authuser', 'reg_expires', 'transport' ] } } } }, 'reg_expires': { 'cn': '注册过期时间(秒). 范围: 1 - 3600. 例: 180', 'en': 'Register expires(Seconds). range: 1 - 3600. el: 180', 'check': { 'type': 'int', 'method': 'range', 'range': { 'start': 1, 'end': 3600 } } }, 'transport': { 'cn': '网络传输协议. 值: udp | tcp | tls', 'en': 'Network transmission protocol. Value: udp | tcp | tls', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': ['udp', 'tcp', 'tls'] } }, 'autoanswer': { 'cn': '自动接听. 值:yes | no | 10 (延迟10秒应答)', 'en': 'Auto Answer. Value: yes | no | 10 (delay 10 Seconds answer)', 'check': { 'type': 'string', 'empty': False, 'relate': True, 'data_relate': { 'yes': {}, 'no': { 'account_info_3': [ 'ringfile' ] }, 'default': { 'account_info_3': [ 'ringfile' ] } }, 'method': 'default' } }, 'ringfile': { 'cn': '来电铃声. 例: sweet.wav', 'en': 'Ring. el: sweet.wav', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'alarm_tone0.wav', 'alarm_tone1.wav', 'alarm_tone2.wav', 'bell_burglar_ring.wav', 'bells_music_tone.wav', 'bigben.wav', 'digital_telephone.wav', 'emergency_alarm_tone.wav', 'emergency_beep_tone.wav', 'oldphone.wav', 'ring.wav', 'special_music_tone.wav', 'sweet.wav', 'toy.wav', '1.wav', '2.wav', '3.wav', '4.wav', '5.wav' ] } }, 'nat_mode': { 'cn': 'NAT模式. 值: disabled | stun | turn | ice', 'en': 'NAT Mode. Value: disabled | stun | turn | ice', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'disabled', 'stun', 'turn', 'ice' ] } }, 'stun_server': { 'cn': '服务器地址.', 'en': 'Server address.', 'check': { 'type': 'string', 'method': 'regex_domain' } }, 'stun_port': { 'cn': '服务器端口. 范围: 1 - 65535. 例: 3478', 'en': 'Server port. range: 1 - 65535. el: 3478', 'check': { 'type': 'int', 'method': 'range', 'range': { 'start': 1, 'end': 65535 } } }, 'nat_username': { 'cn': 'NAT用户名.', 'en': 'NAT username.', 'check': { 'type': 'string', 'method': 'default' } }, 'nat_password': { 'cn': 'NAT账号密码.', 'en': 'NAT account password.', 'check': { 'type': 'string', 'method': 'default' } } }, 'account_info_p2p': { 'account_info_p2p': { 'cn': 'P2P账号配置.', 'en': 'P2P account settings.' }, 'username': { 'cn': 'p2p账号. 例: 1001', 'en': 'p2p account. el: 1001', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'enable': { 'cn': '启动p2p账号. 值:yes | no', 'en': 'Enable p2p account. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'account_info_p2p': [ 'username', ] } } } }, 'autoanswer': { 'cn': '自动接听. 值:yes | no | 10 (延迟10秒应答)', 'en': 'Auto Answer. Value: yes | no | 10 (delay 10 Seconds answer)', 'check': { 'type': 'string', 'empty': False, 'relate': True, 'data_relate': { 'yes': {}, 'no': { 'account_info_p2p': [ 'ringfile' ] }, 'default': { 'account_info_p2p': [ 'ringfile' ] } }, 'method': 'default' } }, 'ringfile': { 'cn': '来电铃声. 例: sweet.wav', 'en': 'Ring. el: sweet.wav', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'alarm_tone0.wav', 'alarm_tone1.wav', 'alarm_tone2.wav', 'bell_burglar_ring.wav', 'bells_music_tone.wav', 'bigben.wav', 'digital_telephone.wav', 'emergency_alarm_tone.wav', 'emergency_beep_tone.wav', 'oldphone.wav', 'ring.wav', 'special_music_tone.wav', 'sweet.wav', 'toy.wav', '1.wav', '2.wav', '3.wav', '4.wav', '5.wav' ] } } }, 'sip_advance': { 'sip_advance': { 'cn': 'SIP高级设置.', 'en': 'Advance SIP Settings.' }, 'localport': { 'cn': 'SIP本地端口. 范围: 1 - 65535. 例: 5060', 'en': 'System local port. range: 1 - 65535. el: 5060', 'check': { 'type': 'int', 'empty': False, 'method': 'range', 'range': { 'start': 1, 'end': 65535 } } }, 'rtpstartport': { 'cn': 'RTP开始端口. 范围: 1 - 65535. 例: 5060', 'en': 'RTP start port. range: 1 - 65535. el: 5060', 'check': { 'type': 'int', 'empty': False, 'method': 'range', 'range': { 'start': 1, 'end': 65535 } } }, 'rtpendport': { 'cn': 'RTP结束端口. 范围: 1 - 65535. 例: 5060', 'en': 'RTP end port. range: 1 - 65535. el: 5060', 'check': { 'type': 'int', 'empty': False, 'method': 'range', 'range': { 'start': 1, 'end': 65535 } } }, 'rtptimeout': { 'cn': 'RTP超时时间(秒). 范围: 1 - 3600. 例: 60', 'en': 'RTP timeout(Seconds) range: 1 - 3600. el: 60', 'check': { 'type': 'int', 'empty': False, 'method': 'range', 'range': { 'start': 1, 'end': 3600 } } } }, 'codec': { 'codec': { 'cn': '音频编码设置.', 'en': 'Audio Codecs Settings.' }, 'g722': { 'cn': '开启G722编码. 值: yes | no', 'en': 'Enable G722 codec. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'alaw': { 'cn': '开启G711(alaw)编码. 值: yes | no', 'en': 'Enable G711(alaw) codec. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'ulaw': { 'cn': '开启G711(ulaw)编码. 值: yes | no', 'en': 'Enable G711(ulaw) codec. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'opus': { 'cn': '开启OPUS编码. 值: yes | no', 'en': 'Enable OPUS codec. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } } }, 'intercom': { 'intercom': { 'cn': '对讲及拨号设置.', 'en': 'Press to talk Settings.' }, 'onekey_num': { 'cn': '一键对讲号码. 例: 101', 'en': 'One-key intercom number. el: 101', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'onekey_1_num': { 'cn': '一键对讲号码 1. 例: 101', 'en': 'One-key intercom number 1. el: 101', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'onekey_2_num': { 'cn': '一键对讲号码 2. 例: 102', 'en': 'One-key intercom number 2. el: 102', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'onekey_line': { 'cn': '一键呼出线路. 值: auto | account_info_1 | account_info_2 | account_info_3 | p2p', 'en': 'One-key outgoing line. Value: auto | account_info_1 | account_info_2 | account_info_3 | p2p', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'auto', 'account_info_1', 'account_info_2', 'account_info_3', 'p2p' ] } }, 'onekey_1_line': { 'cn': '号码1呼出线路. 值: auto | account_info_1 | account_info_2 | account_info_3 | p2p', 'en': 'One-key outgoing line. Value: auto | account_info_1 | account_info_2 | account_info_3 | p2p', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'auto', 'account_info_1', 'account_info_2', 'account_info_3', 'p2p' ] } }, 'onekey_2_line': { 'cn': '号码2呼出线路. 值: auto | account_info_1 | account_info_2 | account_info_3 | p2p', 'en': 'One-key outgoing line. Value: auto | account_info_1 | account_info_2 | account_info_3 | p2p', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'auto', 'account_info_1', 'account_info_2', 'account_info_3', 'p2p' ] } }, 'repress_cancel': { 'cn': '再次按键取消呼叫. 值:yes | no', 'en': 'Press again to cancel the call. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } } }, 'input_detect': { 'input_detect': { 'cn': '输入口设置.', 'en': 'Sensor Settings.' }, 'enable': { 'cn': '启动输入检测. 值:yes | no', 'en': 'Enable input check. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'input_detect': [ 'level' ] } } } }, 'level': { 'cn': '触发模式(上升沿触发或下降沿触发). 值:low | high', 'en': 'Trigger mode (rising edge trigger or falling edge trigger). Value: cn | en', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': ['low', 'high'] } }, 'trigger_dial_enable': { 'cn': '启动触发拨号 值:yes | no', 'en': 'Enable trigger dial. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'input_detect': [ 'trigger_num' ] } } } }, 'trigger_num': { 'cn': '触发拨号的目的号码 例: 101', 'en': 'The destination number that triggers the dialing. el: 101', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'trigger_line': { 'cn': '呼出线路. 值: auto | account_info_1 | account_info_2 | account_info_3 | p2p', 'en': 'Outgoing line. Value: auto | account_info_1 | account_info_2 | account_info_3 | p2p', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'auto', 'account_info_1', 'account_info_2', 'account_info_3', 'p2p' ] } } }, 'output': { 'output': { 'cn': '输出口设置.', 'en': 'Relay Control Settings.' }, 'mode': { 'cn': '触发模式. 值: delay | hangup | answered', 'en': 'Trigger mode. Value: delay | hangup | answered', 'check': { 'type': 'string', 'empty': False, 'method': 'enums', 'data_enums': ['delay', 'hangup', 'answered'] } }, 'duration': { 'cn': '持续时间(秒). 范围: 1 - 600 例: 10', 'en': 'Duration(Seconds). range: 1 - 600 el: 10', 'check': { 'type': 'int', 'empty': False, 'method': 'range', 'range': { 'start': 1, 'end': 600 } } }, 'type': { 'cn': '触发类型. 值: On | FastFlashing | SlowFlashing', 'en': 'Trigger type. Value: On | FastFlashing | SlowFlashing', 'check': { 'type': 'string', 'empty': False, 'method': 'enums', 'data_enums': ['On', 'FastFlashing', 'SlowFlashing'] } } }, 'trigger': { 'trigger': { 'cn': '触发设置.', 'en': 'Trigger Settings.' }, 'input': { 'cn': '启动输入触发. 值: yes | no', 'en': 'Enable input trigger. Value: yes | no', 'check': { 'type': 'string', 'empty': False, } }, 'dtmf': { 'cn': '启动DTMF触发. 值: yes | no', 'en': 'Enable DTMF trigger. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'trigger': [ 'dtmf_code' ] } } } }, 'dtmf_code': { 'cn': 'DTMF触发特征码. 例: 1*', 'en': 'DTMF trigger feature code. Value: 1*', 'check': { 'type': 'string', 'method': 'regex_dtmf' } }, 'call_state': { 'cn': '启动通话状态触发. 值: yes | no', 'en': 'Enable Call State trigger. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'trigger': [ 'state_info' ] } } } }, 'state_info': { 'cn': '设定触发的通话状态. 值: outgoing | incoming | hangup | both | answered', 'en': 'Set the triggered call status. Value: outgoing | incoming | hangup | both | answered', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'outgoing', 'incoming', 'hangup', 'both', 'answered' ] } } }, 'multicast_player': { 'multicast_player': { 'cn': '组播设置.', 'en': 'Multicast Settings.' }, 'enable': { 'cn': '启动主播监听. 值: yes | no', 'en': 'Enable multicast. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'address1': { 'cn': '组播地址1. 例: rtp://@239.168.12.1:2000', 'en': 'Multicast address 1. el: rtp://@239.168.12.1:2000', 'check': { 'type': 'string', 'method': 'regex_multicast' } }, 'name1': { 'cn': '组播地址1名称. 例: Background-Music-1', 'en': 'Multicast address 1 name. el: Background-Music-1', 'check': { 'type': 'string', 'method': 'default' } }, 'gpio1': { 'cn': 'GPIO触发. 值: Disabled | On | FastFlashing | SlowFlashing', 'en': 'GPIO Trigger. Value: Disabled | On | FastFlashing | SlowFlashing', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'Disabled', 'On', 'FastFlashing', 'SlowFlashing' ] } }, 'address2': { 'cn': '组播地址2. 例: rtp://@239.168.12.2:2000', 'en': 'Multicast address 2. el: rtp://@239.168.12.2:2000', 'check': { 'type': 'string', 'method': 'regex_multicast' } }, 'name2': { 'cn': '组播地址2名称. 例: Background-Music-2', 'en': 'Multicast address 2 name. el: Background-Music-2', 'check': { 'type': 'string', 'method': 'default' } }, 'gpio2': { 'cn': 'GPIO触发. 值: Disabled | On | FastFlashing | SlowFlashing', 'en': 'GPIO Trigger. Value: Disabled | On | FastFlashing | SlowFlashing', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'Disabled', 'On', 'FastFlashing', 'SlowFlashing' ] } }, 'address3': { 'cn': '组播地址3. 例: rtp://@239.168.12.3:2000', 'en': 'Multicast address 3. el: rtp://@239.168.12.3:2000', 'check': { 'type': 'string', 'method': 'regex_multicast' } }, 'name3': { 'cn': '组播地址3名称. 例: Background-Music-3', 'en': 'Multicast address 3 name. el: Background-Music-3', 'check': { 'type': 'string', 'method': 'default' } }, 'gpio3': { 'cn': 'GPIO触发. 值: Disabled | On | FastFlashing | SlowFlashing', 'en': 'GPIO Trigger. Value: Disabled | On | FastFlashing | SlowFlashing', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'Disabled', 'On', 'FastFlashing', 'SlowFlashing' ] } }, 'address4': { 'cn': '组播地址4. 例: rtp://@239.168.12.4:2000', 'en': 'Multicast address 4. el: rtp://@239.168.12.4:2000', 'check': { 'type': 'string', 'method': 'regex_multicast' } }, 'name4': { 'cn': '组播地址4名称. 例: Background-Music-4', 'en': 'Multicast address 4 name. el: Background-Music-4', 'check': { 'type': 'string', 'method': 'default' } }, 'gpio4': { 'cn': 'GPIO触发. 值: Disabled | On | FastFlashing | SlowFlashing', 'en': 'GPIO Trigger. Value: Disabled | On | FastFlashing | SlowFlashing', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'Disabled', 'On', 'FastFlashing', 'SlowFlashing' ] } }, 'address5': { 'cn': '组播地址5. 例: rtp://@239.168.12.5:2000', 'en': 'Multicast address 5. el: rtp://@239.168.12.5:2000', 'check': { 'type': 'string', 'method': 'regex_multicast' } }, 'name5': { 'cn': '组播地址5名称. 例: Background-Music-5', 'en': 'Multicast address 5 name. el: Background-Music-5', 'check': { 'type': 'string', 'method': 'default' } }, 'gpio5': { 'cn': 'GPIO触发. 值: Disabled | On | FastFlashing | SlowFlashing', 'en': 'GPIO Trigger. Value: Disabled | On | FastFlashing | SlowFlashing', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'Disabled', 'On', 'FastFlashing', 'SlowFlashing' ] } }, 'address6': { 'cn': '组播地址6. 例: rtp://@239.168.12.6:2000', 'en': 'Multicast address 6. el: rtp://@239.168.12.6:2000', 'check': { 'type': 'string', 'method': 'regex_multicast' } }, 'name6': { 'cn': '组播地址6名称. 例: Background-Music-6', 'en': 'Multicast address 6 name. el: Background-Music-6', 'check': { 'type': 'string', 'method': 'default' } }, 'gpio6': { 'cn': 'GPIO触发. 值: Disabled | On | FastFlashing | SlowFlashing', 'en': 'GPIO Trigger. Value: Disabled | On | FastFlashing | SlowFlashing', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'Disabled', 'On', 'FastFlashing', 'SlowFlashing' ] } }, 'address7': { 'cn': '组播地址7. 例: rtp://@239.168.12.7:2000', 'en': 'Multicast address 7. el: rtp://@239.168.12.7:2000', 'check': { 'type': 'string', 'method': 'regex_multicast' } }, 'name7': { 'cn': '组播地址7名称. 例: Background-Music-7', 'en': 'Multicast address 7 name. el: Background-Music-7', 'check': { 'type': 'string', 'method': 'default' } }, 'gpio7': { 'cn': 'GPIO触发. 值: Disabled | On | FastFlashing | SlowFlashing', 'en': 'GPIO Trigger. Value: Disabled | On | FastFlashing | SlowFlashing', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'Disabled', 'On', 'FastFlashing', 'SlowFlashing' ] } }, 'address8': { 'cn': '组播地址8. 例: rtp://@239.168.12.8:2000', 'en': 'Multicast address 8. el: rtp://@239.168.12.8:2000', 'check': { 'type': 'string', 'method': 'regex_multicast' } }, 'name8': { 'cn': '组播地址8名称. 例: Background-Music-8', 'en': 'Multicast address 8 name. el: Background-Music-8', 'check': { 'type': 'string', 'method': 'default' } }, 'gpio8': { 'cn': 'GPIO触发. 值: Disabled | On | FastFlashing | SlowFlashing', 'en': 'GPIO Trigger. Value: Disabled | On | FastFlashing | SlowFlashing', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'Disabled', 'On', 'FastFlashing', 'SlowFlashing' ] } }, 'address9': { 'cn': '组播地址9. 例: rtp://@239.168.12.9:2000', 'en': 'Multicast address 9. el: rtp://@239.168.12.9:2000', 'check': { 'type': 'string', 'method': 'regex_multicast' } }, 'name9': { 'cn': '组播地址9名称. 例: Background-Music-9', 'en': 'Multicast address 9 name. el: Background-Music-9', 'check': { 'type': 'string', 'method': 'default' } }, 'gpio9': { 'cn': 'GPIO触发. 值: Disabled | On | FastFlashing | SlowFlashing', 'en': 'GPIO Trigger. Value: Disabled | On | FastFlashing | SlowFlashing', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'Disabled', 'On', 'FastFlashing', 'SlowFlashing' ] } } }, 'secret': { 'secret': { 'cn': 'WEB密码设置.', 'en': 'WEB password settings.' }, 'ui_password': { 'cn': 'WEB UI密码. 例: jkcjsdksd', 'en': 'WEB UI password. el: jkcjsdksd', 'check': { 'type': 'string', 'empty': False, 'method': 'default' } } }, 'ipc': { 'ipc': { 'cn': '视频设置.', 'en': 'Video settings.' }, 'url': { 'cn': '画面效果. 例: 0(清晰)', 'en': 'Effect Controls. el: 0(Standard)', 'check': { 'type': 'string', 'method': 'default' } }, 'payload': { 'cn': 'H.264载荷类型. 例: 99', 'en': 'H.264 Payload Type. el: 99', 'check': { 'type': 'int', 'empty': False, 'method': 'range', 'range': { 'start': 96, 'end': 127 } } }, 'rtsp_access': { 'cn': 'RTSP访问. 值: yes | no', 'en': 'RTSP Access. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } } }, 'audio_collection': { 'audio_collection': { 'cn': '音源采集设置.', 'en': 'Audio Collection settings.' }, 'enable': { 'cn': '开启音源采集. 值: yes | no.', 'en': 'Enable Collection. Value: yes | no.', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'audio_collection': [ 'server', 'username', 'password', 'name' ] } } } }, 'server': { 'cn': '服务器地址.', 'en': 'Server address.', 'check': { 'type': 'string', 'method': 'regex_domain' } }, 'username': { 'cn': '用户名.', 'en': 'Username.', 'check': { 'type': 'string', 'method': 'regex_username' } }, 'password': { 'cn': '密码.', 'en': 'Password.', 'check': { 'type': 'string', 'method': 'default' } }, 'name': { 'cn': '音源名称.', 'en': 'Source Name.', 'check': { 'type': 'string', 'method': 'default' } } }, 'volume': { 'volume': { 'cn': '系统音量设置.', 'en': 'System volume settings.' }, 'agc': { 'cn': 'MIC自动增益. 值: yes | no', 'en': 'MIC automatic gain. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, 'relate': True, 'data_relate': { 'yes': { 'volume': [ 'agc_min_volume', 'agc_max_volume' ] } } } }, 'volume_in': { 'cn': '麦克风音量. 范围: 0 - 9. el: 5', 'en': 'Microphone volume. range: 0 - 9. el: 5', 'check': { 'type': 'int', 'empty': False, 'method': 'range', 'range': { 'start': 0, 'end': 9 } } }, 'volume_out': { 'cn': '扬声器音量. 范围: 0 - 9. el: 5', 'en': 'Speaker volume. range: 0 - 9. el: 5', 'check': { 'type': 'int', 'empty': False, 'method': 'range', 'range': { 'start': 0, 'end': 9 } } }, 'agc_min_volume': { 'cn': '自动增益最小音量. 范围: 0 - 9. el: 2', 'en': 'AGC Min Volume. range: 0 - 9. el: 2', 'check': { 'type': 'int', 'empty': False, 'method': 'range', 'range': { 'start': 0, 'end': 9 } } }, 'agc_max_volume': { 'cn': '自动增益最大音量. 范围: 0 - 9. el: 2', 'en': 'AGC Max Volume. range: 0 - 9. el: 2', 'check': { 'type': 'int', 'empty': False, 'method': 'range', 'range': { 'start': 0, 'end': 9 } } }, 'key_beep': { 'cn': '按键音启动. 值: yes | no', 'en': 'Key beep enable. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } } }, 'auto_privisioning': { 'auto_privisioning': { 'cn': '自动配置设置.', 'en': 'Auto Privisioning settings.' }, 'address': { 'cn': '服务器地址. 例: 10.10.1.15 或 http://192.168.1.100/files/', 'en': 'Server address. el: 10.10.1.15() or http://192.168.1.100/files/', 'check': { 'type': 'string', 'method': 'default' } }, 'mode': { 'cn': '访问模式. 值: tftp | http', 'en': 'Access mode. Value: tftp | http', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'tftp', 'http' ] } }, 'format': { 'cn': '配置文件格式. 值: json | yaml', 'en': 'Configuration file format. Value: json | yaml', 'check': { 'type': 'string', 'method': 'enums', 'data_enums': [ 'json', 'yaml' ] } } }, 'call_action_url': { 'call_action_url': { 'cn': '呼叫事件回调URL设置.', 'en': 'Call event callback URL settings.' }, 'incoming_enable': { 'cn': '呼入回调URL启动. 值: yes | no', 'en': 'Incoming callback URL enable. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'incoming_url': { 'cn': '呼入回调URL. el: http://192.168.1.100/incoming', 'en': 'Incoming callback URL. el: http://192.168.1.100/incoming', 'check': { 'type': 'string', 'method': 'default', } }, 'outgoing_enable': { 'cn': '呼出回调URL启动. 值: yes | no', 'en': 'Outgoing callback URL enable. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'outgoing_url': { 'cn': '呼出回调URL. el: http://192.168.1.100/outgoing', 'en': 'Outgoing callback URL. el: http://192.168.1.100/outgoing', 'check': { 'type': 'string', 'method': 'default', } }, 'answered_enable': { 'cn': '应答回调URL启动. 值: yes | no', 'en': 'Answered callback URL enable. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'answered_url': { 'cn': '应答回调URL. el: http://192.168.1.100/answered', 'en': 'Answered callback URL. el: http://192.168.1.100/answered', 'check': { 'type': 'string', 'method': 'default', } }, 'hangup_enable': { 'cn': '挂断回调URL启动. 值: yes | no', 'en': 'Hangup callback URL enable. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'hangup_url': { 'cn': '挂断回调URL. el: http://192.168.1.100/hangup', 'en': 'Hangup callback URL. el: http://192.168.1.100/hangup', 'check': { 'type': 'string', 'method': 'default', } } }, 'relay_action_url': { 'relay_action_url': { 'cn': '继电器事件回调URL设置.', 'en': 'Relay event callback URL settings.' }, 'on_enable': { 'cn': '继电器开回调URL启动. 值: yes | no', 'en': 'Relay on callback URL enable. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'on_url': { 'cn': '继电器开回调URL. el: http://192.168.1.100/on', 'en': 'Relay on callback URL. el: http://192.168.1.100/on', 'check': { 'type': 'string', 'method': 'default', } }, 'off_enable': { 'cn': '继电器关回调URL启动. 值: yes | no', 'en': 'Relay off callback URL enable. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'off_url': { 'cn': '继电器关回调URL. el: http://192.168.1.100/off', 'en': 'Relay off callback URL. el: http://192.168.1.100/off', 'check': { 'type': 'string', 'method': 'default', } } }, 'api': { 'api': { 'cn': 'API设置.', 'en': 'API settings.' }, 'call_enable': { 'cn': '呼叫API启动. 值: yes | no', 'en': 'Call API enable. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'relay_enable': { 'cn': '继电器API启动. 值: yes | no', 'en': 'Relay API enable. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } }, 'player_enable': { 'cn': '播放器API启动. 值: yes | no', 'en': 'Player API enable. Value: yes | no', 'check': { 'type': 'bool', 'empty': False, } } }, 'default': { 'cn': '', 'en': '' } } if not hasattr(ruamel.yaml.comments.CommentedMap, "yaml_set_comment_before_key"): def my_yaml_set_comment_before_key(self, key, comment, column=None, clear=False): """ append comment to list of comment lines before key, '# ' is inserted before the comment column: determines indentation, if not specified take indentation from previous comment, otherwise defaults to 0 clear: if True removes any existing comments instead of appending """ key_comment = self.ca.items.setdefault(key, [None, [], None, None]) if clear: key_comment[1] = [] comment_list = key_comment[1] if comment: comment_start = '# ' if comment[-1] == '\n': comment = comment[:-1] # strip final newline if there else: comment_start = '#' if column is None: if comment_list: # if there already are other comments get the column from them column = comment_list[-1].start_mark.column else: column = 0 # start_mark = ruamel.yaml.error.CommentMark(None, None, None, column, None, None) start_mark = ruamel.yaml.error.CommentMark(column) comment_list.append(ruamel.yaml.tokens.CommentToken( comment_start + comment + '\n', start_mark, None)) return self ruamel.yaml.comments.CommentedMap.yaml_set_comment_before_key = \ my_yaml_set_comment_before_key def deleteKeys(data = {}, keys = origin_keys): ret_data = data for key, value in keys.items(): if key in ret_data: for k in value: if k in ret_data[key]: del ret_data[key][k] if not ret_data[key]: del ret_data[key] return ret_data def addComment(data, commentkeys = comment_data): tmp_data = data for key, value in tmp_data.items(): try: if "cn" in commentkeys[key][key].keys() and "en" in commentkeys[key][key].keys(): tmp_data.yaml_set_comment_before_key(key, commentkeys[key][key]["cn"], column=0) tmp_data.yaml_set_comment_before_key(key, commentkeys[key][key]["en"], column=0) except: tmp_data.yaml_set_comment_before_key(key, commentkeys['default']["cn"], column=0) # tmp_data.yaml_set_comment_before_key(key, commentkeys['default']["en"], column=0) cm = tmp_data[key] for k, v in value.items(): try: if "cn" in commentkeys[key][k].keys() and "en" in commentkeys[key][k].keys(): cm.yaml_set_comment_before_key(k, commentkeys[key][k]["cn"], column=2) cm.yaml_set_comment_before_key(k, commentkeys[key][k]["en"], column=2) except: cm.yaml_set_comment_before_key(k, commentkeys['default']["cn"], column=2) # cm.yaml_set_comment_before_key(k, commentkeys['default']["en"], column=2) return tmp_data def exportIni2Yaml(src = SYSTEM_CONF_PATH, dest = TMPFILE): ini = configparser.RawConfigParser() ini.read(src, encoding='utf-8') if ini.has_section("firmware"): ini.remove_section("firmware") ini_data = ini._sections final_data = deleteKeys(ini_data) data = ruamel.yaml.round_trip_load(ruamel.yaml.round_trip_dump(final_data)) final_data = addComment(data) with open(dest, 'wb') as f: ruamel.yaml.round_trip_dump(final_data, f, encoding='utf-8') os.system("/etc/scripts/action.sh set_yaml") def exportIni2Json(src = SYSTEM_CONF_PATH, dest = TMPFILE): ini = configparser.RawConfigParser() ini.read(src, encoding='utf-8') if ini.has_section("firmware"): ini.remove_section("firmware") ini_data = ini._sections final_data = deleteKeys(ini_data) with open(dest, 'w', encoding='utf-8') as f: json.dump(final_data, f, indent=4) def checkData(data, section, key, value, check): res = {} if check['type'] == 'string': if 'empty' not in check: if not value: return None else: if check['empty'] and not value: return None if check['method'] == 'enums': for enum in check['data_enums']: if enum == value: return None res.setdefault("check", "%s.%s" % (section, key)) return res elif check['method'] == 'regex_ip': if re.match(r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", value): return None else: res.setdefault("check", "%s.%s" % (section, key)) # res.setdefault("status", "irregular") return res elif check['method'] == 'regex_domain': if re.match(r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9-]*[A-Za-z0-9])$", value): return None else: res.setdefault("check", "%s.%s" % (section, key)) # res.setdefault("status", "irregular") return res elif check['method'] == 'regex_username': if re.match(r"^[a-zA-Z0-9.#@*][a-zA-Z0-9.#@*]{0,16}$", value): return None else: res.setdefault("check", "%s.%s" % (section, key)) # res.setdefault("status", "irregular") return res elif check['method'] == 'regex_dtmf': if re.match(r"^[0-9][0-9*]{0,16}$", value): return None else: res.setdefault("check", "%s.%s" % (section, key)) # res.setdefault("status", "irregular") return res elif check['method'] == 'regex_multicast': if re.match(r"rtp://@2(?:2[4-9]|3\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d?|0)){3}\:([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])$", value): return None else: res.setdefault("check", "%s.%s" % (section, key)) # res.setdefault("status", "irregular") return res elif check['method'] == 'secret': if re.match(r"^[a-zA-Z]\w{4,16}$", value): return None else: res.setdefault("check", "%s.%s" % (section, key)) # res.setdefault("status", "irregular") return res else: pass if 'relate' in check and check['relate']: for m, n in check['data_relate'].items(): if m == value: if n: for x, y in n.items(): for i in y: if not data[x][i] or data[x][i] == '': res.setdefault("check", "%s.%s" % (x, i)) # res.setdefault("status", "relate_irregular") return res return None try: number = float(value) if number <= 1 or number >= 3600: res.setdefault("check", "%s.%s" % (section, key)) # res.setdefault("status", "relate_irregular") return res for x, y in check['data_relate']['default'].items(): for i in y: if data[x][i] == '' or not data[x][i]: res.setdefault("check", "%s.%s" % (x, i)) # res.setdefault("status", "relate_irregular") return res return None except: res.setdefault("check", "%s.%s" % (section, key)) # res.setdefault("status", "relate_irregular") return res return None return None elif check['type'] == 'int': if not value: return None try: if check['range']['start'] <= int(value) and check['range']['end'] >= int(value): return None else: res.setdefault("check", "%s.%s" % (section, key)) # res.setdefault("status", "irregular") return res except: res.setdefault("check", "%s.%s" % (section, key)) # res.setdefault("status", "irregular") return res elif check['type'] == 'bool': if 'empty' not in check: if not value: return None else: if check['empty'] and not value: return None if value == 'yes' or value == 'no': if 'relate' in check and check['relate']: for m, n in check['data_relate'].items(): if m == value: if n: for x, y in n.items(): for i in y: if not data[x][i] or data[x][i] == '': res.setdefault("check", "%s.%s" % (x, i)) # res.setdefault("status", "relate_irregular") return res return None return None else: res.setdefault("check", "%s.%s" % (section, key)) # res.setdefault("status", "relate_irregular") return res else: return None def output_str(res): target = {} if "check" in res: target.setdefault("check", res["check"]) else: target.setdefault("check", "unknown") return json.dumps(target) def handleFirmwareUrl(url): if not url: return config_firmware = configparser.RawConfigParser() config_firmware.read(SYSTEM_CONF_PATH[3]) old_url = config_firmware.get("firmware", "url") if url == old_url: return else: config_firmware.set("firmware", "url", url) with open(SYSTEM_CONF_PATH[3], 'w') as f: config_firmware.write(f, space_around_delimiters=False) config_system = configparser.RawConfigParser() config_system.read(SYSTEM_CONF_PATH[0]) config_system.set("upgrade", "date", time.strftime("%Y-%m-%d")) with open(SYSTEM_CONF_PATH[0], 'w') as f: config_system.write(f, space_around_delimiters=False) os.system("/etc/scripts/onvif_upgrade.sh upgrade %s no" % url) def importConfig(src = TMPFILE, dest = SYSTEM_CONF_PATH): try: with open(src, 'r', encoding='utf-8') as f: data = json.loads(f.read()) except FileNotFoundError: res = {} res.setdefault("check", "file_not_found") print(output_str(res)) sys.exit(0) except: try: with open(src, 'rb') as f: yaml=ruamel.yaml.YAML(typ='safe') data = yaml.load(f) except: res = {} res.setdefault("check", "file_format_error") print(output_str(res)) os.remove(src) sys.exit(0) if type(data) != dict: res = {} res.setdefault("check", "file_format_error") print(output_str(res)) os.remove(src) sys.exit(0) output_data = deleteKeys(data) for key, value in output_data.items(): for k, v in value.items(): # try: # res = checkData(output_data, key, k, v, comment_data[key][k]['check']) # if res: # print(output_str(res)) # os.remove(TMPFILE) # sys.exit(0) # except SystemExit: # sys.exit(0) # except KeyError: # continue if not v: output_data[key][k] = '' if "volume" in output_data: volume_data = {} volume_data.setdefault("volume", output_data["volume"]) del output_data["volume"] if "priority" in output_data: volume_data.setdefault("priority", output_data["priority"]) del output_data["priority"] config_volume = configparser.RawConfigParser() config_volume.read(dest[1], encoding='utf-8') config_volume.read_dict(volume_data) with open(dest[1], 'w', encoding='utf-8') as f: config_volume.write(f, space_around_delimiters=False) if "schedule_1" in output_data: schedule_data = {} for i in range(1, 31): test_section="schedule_"+str(i) if test_section in output_data: schedule_data.setdefault(test_section, output_data[test_section]) del output_data[test_section] if "holidays" in output_data: schedule_data.setdefault("holidays", output_data["holidays"]) del output_data["holidays"] if "screen_preset" in output_data: schedule_data.setdefault("screen_preset", output_data["screen_preset"]) del output_data["screen_preset"] if "call_screen" in output_data: schedule_data.setdefault("call_screen", output_data["call_screen"]) del output_data["call_screen"] config_schedule = configparser.RawConfigParser() config_schedule.read(dest[2], encoding='utf-8') config_schedule.read_dict(schedule_data) with open(dest[2], 'w', encoding='utf-8') as f: config_schedule.write(f, space_around_delimiters=False) config_system = configparser.RawConfigParser() config_system.read(dest[0], encoding='utf-8') config_system.read_dict(output_data) with open(dest[0], 'w', encoding='utf-8') as f: config_system.write(f, space_around_delimiters=False) if "firmware" in data and "url" in data["firmware"] and data["firmware"]["url"]: handleFirmwareUrl(data["firmware"]["url"]) if __name__ == "__main__": if len(sys.argv) == 2: action=sys.argv[1] else: res = {} res.setdefault("check", "Usage: %s export_yaml | export_json | import" % (sys.argv[0])) print(output_str(res)) sys.exit(0) if action == 'export_yaml': exportIni2Yaml() elif action == 'export_json': exportIni2Json() elif action == 'import': importConfig() os.remove(TMPFILE) else: res = {} res.setdefault("check", "not_support_action") print(output_str(res)) sys.exit(0)