Inverter Voltronic: различия между версиями

Материал из noname.com.ua
Перейти к навигацииПерейти к поиску
 
(не показаны 3 промежуточные версии этого же участника)
Строка 3: Строка 3:
 
[[Категория:UPS]]
 
[[Категория:UPS]]
 
=Вольтроник=
 
=Вольтроник=
  +
=Внешний вид=
  +
[[Файл:Voltrronic 2E VP.JPG|200px]]
  +
<BR>
 
=Инструкция=
 
=Инструкция=
  +
<BR>
 
[[Файл:Axpert V PF1 manual.pdf]]
 
[[Файл:Axpert V PF1 manual.pdf]]
  +
<BR>
   
 
=Снимать данные с линукса=
 
=Снимать данные с линукса=
Строка 216: Строка 221:
 
==<code>mpp-nut-bridge.service</code>==
 
==<code>mpp-nut-bridge.service</code>==
 
{{#spoiler:mpp-nut-bridge.service|
 
{{#spoiler:mpp-nut-bridge.service|
  +
<BR>
 
* <code>/usr/local/virtualenvs/mppsolar/bin/python3 </code> - этот путь к <code>virtualenv</code>
 
* <code>/usr/local/virtualenvs/mppsolar/bin/python3 </code> - этот путь к <code>virtualenv</code>
  +
<BR>
 
<PRE>
 
<PRE>
 
[Unit]
 
[Unit]
Строка 254: Строка 261:
 
==<code>ups-snmp-passpersist.py</code>==
 
==<code>ups-snmp-passpersist.py</code>==
 
{{#spoiler:ups-snmp-passpersist.py|
 
{{#spoiler:ups-snmp-passpersist.py|
  +
<BR>
  +
Тоже написано на скорую руку и с хардкодом - но за основу сойдет
  +
<BR>
 
<PRE>
 
<PRE>
  +
#!/usr/bin/env python3
  +
  +
import sys
  +
import subprocess
  +
import logging
  +
from logging.handlers import RotatingFileHandler
  +
  +
LOGFILE = "/var/log/snmp/snmp-ups.log"
  +
  +
  +
UPS_NAME = "mpp"
  +
  +
def get_oid_data(nut, nut_key):
  +
logger.debug(f"[get_oid_data] nut: {nut}. nut_key: {nut_key}")
  +
val = nut.get(nut_key)
  +
logger.debug(f"[get_oid_data] value: {val}")
  +
return str(val)
  +
  +
  +
def batt_status(nut, nut_key):
  +
val = get_oid_data(nut, nut_key)
  +
s = str(batt_status_code(val))
  +
logger.debug("[batt_status] Status: {s}")
  +
return s
  +
  +
  +
  +
# OID'ы из UPS-MIB (RFC1628)
  +
OIDS = {
  +
# upsIdent*
  +
"1.3.6.1.2.1.33.1.1.1.0": {
  +
"name": "UPS-MIB::upsIdentManufacturer.0",
  +
"type": "string",
  +
"get_oid_method": lambda: "VOLTRONIC"
  +
},
  +
"1.3.6.1.2.1.33.1.1.2.0": {
  +
"name": "UPS-MIB::upsIdentModel.0",
  +
"type": "string",
  +
"get_oid_method": lambda: "2E-VP-5K48"
  +
},
  +
#1.3.6.1.2.1.33.1.1.3.0 UPS-MIB::upsIdentUPSSoftwareVersion.0
  +
"1.3.6.1.2.1.33.1.1.3.0": {
  +
"name": "UPS-MIB::upsIdentUPSSoftwareVersion.0",
  +
"type": "string",
  +
"get_oid_method": lambda: "1",
  +
},
  +
#1.3.6.1.2.1.33.1.1.4.0 UPS-MIB::upsIdentAgentSoftwareVersion.0
  +
"1.3.6.1.2.1.33.1.1.5.0": {
  +
"name": "UPS-MIB::upsIdentAgentSoftwareVersion.0",
  +
"type": "string",
  +
"get_oid_method": lambda: "UPS_ID_IS_NOT_SET"
  +
},
  +
  +
"1.3.6.1.2.1.33.1.1.4.0": {
  +
"name": "UPS-MIB::upsIdentName.0",
  +
"type": "string",
  +
"get_oid_method": lambda: "UPS_ID_IS_NOT_SET"
  +
},
  +
# Battery
  +
"1.3.6.1.2.1.33.1.2.1.0": {
  +
"name": "UPS-MIB::upsBatteryStatus.0",
  +
"type": "integer", # enum
  +
"nut_key": "ups.status",
  +
"get_oid_method": batt_status
  +
},
  +
"1.3.6.1.2.1.33.1.2.2.0": {
  +
"name": "UPS-MIB::upsSecondsOnBattery.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "0"
  +
},
  +
"1.3.6.1.2.1.33.1.2.3.0": {
  +
"name": "UPS-MIB::upsEstimatedMinutesRemaining.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "0"
  +
},
  +
"1.3.6.1.2.1.33.1.2.4.0": {
  +
"name": "UPS-MIB::upsEstimatedChargeRemaining.0",
  +
"type": "integer",
  +
#"nut_key": "battery.charge",
  +
"get_oid_method": lambda: "0"
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.2.3.1.0": {
  +
"name": "UPS-MIB::upsEstimatedChargeRemaining.0",
  +
"type": "integer",
  +
#"nut_key": "battery.charge",
  +
"get_oid_method": lambda: "0"
  +
},
  +
"1.3.6.1.2.1.33.1.2.5.0": {
  +
"name": "UPS-MIB::upsBatteryVoltage.0",
  +
"type": "integer", # 0.1 V
  +
"custom_multiplier": 10,
  +
"nut_key": "battery.voltage",
  +
},
  +
"1.3.6.1.2.1.33.1.2.7.0": {
  +
"name": "UPS-MIB::upsBatteryTemperature.0",
  +
"type": "integer",
  +
"nut_key": "inverter_heat_sink_temperature"
  +
},
  +
  +
# Input
  +
"1.3.6.1.2.1.33.1.3.2.0": {
  +
"name": "UPS-MIB::upsInputNumLines.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "1"
  +
},
  +
"1.3.6.1.2.1.33.1.3.3.1.1.1": {
  +
"name": "UPS-MIB::upsInputLineBads.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "1"
  +
},
  +
"1.3.6.1.2.1.33.1.3.1.0": {
  +
"name": "UPS-MIB::upsInputLineBads.0",
  +
"type": "counter32",
  +
"get_oid_method": lambda: "1"
  +
},
  +
"1.3.6.1.2.1.33.1.3.2.1": {
  +
"name": "UPS-MIB::upsInputNumLines.1",
  +
"type": "integer",
  +
"get_oid_method": lambda: "1"
  +
},
  +
"1.3.6.1.2.1.33.1.3.3.1.2.1": {
  +
"name": "UPS-MIB::upsInputFrequency.1",
  +
"type": "integer", # 0.1 Hz
  +
"custom_multiplier": 10,
  +
"nut_key": "input.frequency",
  +
},
  +
"1.3.6.1.2.1.33.1.3.3.1.3.1": {
  +
"name": "UPS-MIB::upsInputVoltage.1",
  +
"type": "integer", # RMS Volts
  +
"nut_key": "input.voltage",
  +
},
  +
"1.3.6.1.2.1.33.1.3.3.1.4.1": {
  +
"name": "UPS-MIB::upsInputCurrent.1",
  +
"type": "integer", # 0.1 A
  +
"custom_multiplier": 10,
  +
#"nut_key": "input.current"
  +
"get_oid_method": lambda: "0"
  +
},
  +
"1.3.6.1.2.1.33.1.3.3.1.5.1": {
  +
"name": "UPS-MIB::upsInputTruePower.1",
  +
"type": "integer", # Watts
  +
"get_oid_method": lambda: "0"
  +
},
  +
# Output
  +
"1.3.6.1.2.1.33.1.4.1.0": {
  +
"name": "UPS-MIB::upsOutputSource.0",
  +
"type": "integer", # enum
  +
"get_oid_method": lambda: "2"
  +
},
  +
"1.3.6.1.2.1.33.1.4.2.0": {
  +
"name": "UPS-MIB::upsOutputFrequency.0",
  +
"type": "integer", # 0.1 Hz
  +
"custom_multiplier": 10,
  +
"nut_key": "output.frequency",
  +
},
  +
"1.3.6.1.2.1.33.1.4.3.0": {
  +
"name": "UPS-MIB::upsOutputNumLines.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "1"
  +
},
  +
"1.3.6.1.2.1.33.1.4.4.1.1.1": {
  +
"name": "UPS-MIB::upsOutputLineIndex.1",
  +
"type": "integer",
  +
"get_oid_method": lambda: "1",
  +
},
  +
"1.3.6.1.2.1.33.1.4.4.1.2.1": {
  +
"name": "UPS-MIB::upsOutputVoltage.1",
  +
"type": "integer", # RMS
  +
"nut_key": "output.voltage",
  +
},
  +
"1.3.6.1.2.1.33.1.4.4.1.3.1": {
  +
"name": "UPS-MIB::upsOutputCurrent.1",
  +
"type": "integer", # 0.1 A
  +
"custom_multiplier": 10,
  +
#"nut_key": "output_current",
  +
"get_oid_method": lambda: "0"
  +
},
  +
"1.3.6.1.2.1.33.1.4.4.1.4.1": {
  +
"name": "UPS-MIB::upsOutputPower.1",
  +
"type": "integer", # Watts
  +
"nut_key": "ups.power"
  +
},
  +
"1.3.6.1.2.1.33.1.4.4.1.5.1": {
  +
"name": "UPS-MIB::upsOutputPercentLoad.1",
  +
"type": "integer", # percent
  +
"nut_key": "ups.load",
  +
},
  +
# Bypass
  +
"1.3.6.1.2.1.33.1.5.1.0": {
  +
"name": "UPS-MIB::upsBypassFrequency.0",
  +
"type": "integer", # 0.1 Hz
  +
"custom_multiplier": 10,
  +
"nut_key": "input.frequency",
  +
},
  +
"1.3.6.1.2.1.33.1.5.2.0": {
  +
"name": "UPS-MIB::upsBypassNumLines.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "1",
  +
},
  +
"1.3.6.1.2.1.33.1.5.3.1.1.1": {
  +
"name": "UPS-MIB::upsBypassLineIndex.1",
  +
"type": "integer",
  +
"get_oid_method": lambda: "1",
  +
},
  +
"1.3.6.1.2.1.33.1.5.3.1.2.1": {
  +
"name": "UPS-MIB::upsBypassVoltage.1",
  +
"type": "integer", # RMS
  +
"nut_key": "input.voltage",
  +
},
  +
"1.3.6.1.2.1.33.1.5.3.1.3.1": {
  +
"name": "UPS-MIB::upsBypassCurrent.1",
  +
"type": "integer", # 0.1 A
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.5.3.1.4.1": {
  +
"name": "UPS-MIB::upsBypassPower.1",
  +
"type": "integer", # Watts
  +
"get_oid_method": lambda: "0",
  +
},
  +
  +
# Alarms
  +
"1.3.6.1.2.1.33.1.6.1.0": {
  +
"name": "UPS-MIB::upsAlarmsPresent.0",
  +
"type": "gauge", # Gauge32
  +
"get_oid_method": lambda: "0",
  +
},
  +
# Tests
  +
"1.3.6.1.2.1.33.1.7.1.0": {
  +
"name": "UPS-MIB::upsTestId.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.7.2.0": {
  +
"name": "UPS-MIB::upsTestSpinLock.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.7.3.0": {
  +
"name": "UPS-MIB::upsTestResultsSummary.0",
  +
"type": "integer", # enum
  +
"get_oid_method": lambda: "1",
  +
},
  +
"1.3.6.1.2.1.33.1.7.4.0": {
  +
"name": "UPS-MIB::upsTestResultsDetail.0",
  +
"type": "string",
  +
"get_oid_method": lambda: "OK PASS",
  +
},
  +
"1.3.6.1.2.1.33.1.7.5.0": {
  +
"name": "UPS-MIB::upsTestStartTime.0",
  +
"type": "timeticks",
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.7.6.0": {
  +
"name": "UPS-MIB::upsTestElapsedTime.0",
  +
"type": "integer", # в твоём выводе APC косячит, но по MIB INTEGER
  +
"get_oid_method": lambda: "0",
  +
},
  +
# Shutdown / control
  +
"1.3.6.1.2.1.33.1.8.1.0": {
  +
"name": "UPS-MIB::upsShutdownType.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "2",
  +
},
  +
"1.3.6.1.2.1.33.1.8.2.0": {
  +
"name": "UPS-MIB::upsShutdownAfterDelay.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "-1",
  +
},
  +
"1.3.6.1.2.1.33.1.8.3.0": {
  +
"name": "UPS-MIB::upsStartupAfterDelay.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "-1",
  +
},
  +
"1.3.6.1.2.1.33.1.8.4.0": {
  +
"name": "UPS-MIB::upsRebootWithDuration.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "-1",
  +
},
  +
"1.3.6.1.2.1.33.1.8.5.0": {
  +
"name": "UPS-MIB::upsAutoRestart.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "2",
  +
},
  +
# Config
  +
"1.3.6.1.2.1.33.1.9.1.0": {
  +
"name": "UPS-MIB::upsConfigInputFreq.0",
  +
"type": "integer", # 0.1 Hz
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.9.2.0": {
  +
"name": "UPS-MIB::upsConfigOutputVoltage.0",
  +
"type": "integer", # RMS Volts
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.9.3.0": {
  +
"name": "UPS-MIB::upsConfigOutputFreq.0",
  +
"type": "integer", # 0.1 Hz
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.9.4.0": {
  +
"name": "UPS-MIB::upsConfigOutputVA.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.9.5.0": {
  +
"name": "UPS-MIB::upsConfigOutputPower.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.9.6.0": {
  +
"name": "UPS-MIB::upsConfigLowBattTime.0",
  +
"type": "integer", # minutes
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.9.7.0": {
  +
"name": "UPS-MIB::upsConfigAudibleStatus.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.9.8.0": {
  +
"name": "UPS-MIB::upsConfigLowVoltageTransferPoint.0",
  +
"type": "integer", # RMS Volts
  +
"get_oid_method": lambda: "0",
  +
},
  +
"1.3.6.1.2.1.33.1.9.9.0": {
  +
"name": "UPS-MIB::upsConfigHighVoltageTransferPoint.0",
  +
"type": "integer", # RMS Volts
  +
"get_oid_method": lambda: "0",
  +
},
  +
#POWERNET-MIB
  +
"1.3.6.1.4.1.318.1.1.1.2.3.1.0": {
  +
"name": "POWERNET-MIB::upsHighPrecBatteryCapacity.0",
  +
"type": "gauge32", # percent
  +
"custom_multiplier": 10,
  +
"nut_key": "battery.charge",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.2.1.3.0": {
  +
"name": "POWERNET-MIB::upsBasicBatteryLastReplaceDate.0",
  +
"type": "string",
  +
"get_oid_method": lambda: "07/07/25",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.2.2.4.0": {
  +
"name": "POWERNET-MIB::upsAdvBatteryReplaceIndicator.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "1",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.2.2.3.0": {
  +
"name": "POWERNET-MIB::upsAdvBatteryRunTimeRemaining.0",
  +
"type": "timeticks",
  +
"get_oid_method": lambda: "1000",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.2.1.1.0": {
  +
"name": "POWERNET-MIB::upsBasicBatteryStatus.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "2",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.2.3.2.0": {
  +
"name": "POWERNET-MIB::upsHighPrecBatteryTemperature.0",
  +
# "type": "gauge32",
  +
# "get_oid_method": lambda: "100",
  +
"custom_multiplier": 10,
  +
"type": "gauge32",
  +
"nut_key": "inverter_heat_sink_temperature"
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.2.3.4.0": {
  +
"name": "POWERNET-MIB::upsHighPrecBatteryActualVoltage.0",
  +
"type": "integer", # 0.1 V
  +
"custom_multiplier": 10,
  +
"nut_key": "battery.voltage",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.2.2.5.0": {
  +
"name": "POWERNET-MIB::upsAdvBatteryNumOfBattPacks.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "1",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.3.2.5.0": {
  +
"name": "POWERNET-MIB::upsAdvInputLineFailCause.0",
  +
"type": "integer",
  +
"get_oid_method": lambda: "10",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.3.3.4.0": {
  +
"name": "POWERNET-MIB::upsHighPrecInputFrequency.0",
  +
"type": "gauge32", # 0.1 Hz
  +
"custom_multiplier": 10,
  +
"nut_key": "input.frequency",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.3.3.1.0": {
  +
"name": "POWERNET-MIB::upsHighPrecInputLineVoltage.0",
  +
"type": "gauge32", # RMS Volts
  +
"custom_multiplier": 10,
  +
"nut_key": "input.voltage",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.1.1.1.0": {
  +
"name": "POWERNET-MIB::upsBasicIdentModel.0",
  +
"type": "string",
  +
"get_oid_method": lambda: "VOLTRONIC 2E-VP-5K48"
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.4.3.4.0": {
  +
"name": "POWERNET-MIB::upsHighPrecOutputCurrent.0",
  +
"custom_multiplier": 10,
  +
#"nut_key": "input.current"
  +
"get_oid_method": lambda: "0",
  +
"type": "gauge32",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.4.3.3.0": {
  +
"name": "POWERNET-MIB::upsHighPrecOutputLoad.0",
  +
"custom_multiplier": 10,
  +
"type": "gauge32", # percent
  +
"nut_key": "ups.load",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.4.1.1.0": {
  +
"name": "POWERNET-MIB::upsBasicOutputStatus.0",
  +
"get_oid_method": lambda: "2",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.4.3.1.0": {
  +
"type": "gauge32", # RMS
  +
"custom_multiplier": 10,
  +
"nut_key": "output.voltage",
  +
},
  +
"1.3.6.1.4.1.318.1.1.1.1.2.3.0": {
  +
"name": "POWERNET-MIB::upsHighPrecOutputVoltage.0",
  +
"type": "string",
  +
"get_oid_method": lambda: "FUCKOFF",
  +
},
  +
}
  +
  +
  +
  +
def get_logger():
  +
logger = logging.getLogger("snmp_test_oid")
  +
if logger.handlers:
  +
return logger
  +
  +
# logger.setLevel(logging.INFO)
  +
logger.setLevel(logging.DEBUG)
  +
  +
handler = RotatingFileHandler(
  +
LOGFILE,
  +
maxBytes=200_000_000,
  +
backupCount=3,
  +
)
  +
fmt = logging.Formatter(
  +
"%(asctime)s [%(levelname)s] %(message)s",
  +
"%Y-%m-%dT%H:%M:%S",
  +
)
  +
handler.setFormatter(fmt)
  +
logger.addHandler(handler)
  +
  +
return logger
  +
  +
  +
logger = get_logger()
  +
  +
# Это нужно что бы сортировать OID правильно
  +
# строковая сортировка в лоб дает неправильнй результат
  +
# ".1.3.6.1.2.1.33.1.10" < ".1.3.6.1.2.1.33.1.2" как строки
  +
# а по SNMP-логике должно быть наоборот (10 > 2).
  +
def oid_key(oid: str):
  +
return tuple(int(x) for x in oid.strip(".").split(".") if x)
  +
  +
def get_nut():
  +
"""Читаем все переменные из upsc mpp@localhost"""
  +
logger.debug(f"[get_nut] Start")
  +
try:
  +
out = subprocess.check_output(
  +
["upsc", f"{UPS_NAME}@localhost"],
  +
stderr=subprocess.DEVNULL,
  +
text=True
  +
)
  +
logger.debug(f"[get_nut]: cmd out: {out}")
  +
except Exception as e:
  +
logger.debug(f"[get_nut]: {e}")
  +
return {}
  +
  +
vals = {}
  +
for line in out.splitlines():
  +
logger.debug(f"[get_nut] Reading line {line}")
  +
if ":" not in line:
  +
continue
  +
k, v = line.split(":", 1)
  +
vals[k.strip()] = v.strip()
  +
logger.debug(f"[get_nut] vals: {vals}")
  +
return vals
  +
  +
  +
def batt_status_code(status_str: str) -> int:
  +
"""
  +
UPS-MIB upsBatteryStatus:
  +
1=unknown, 2=normal, 3=low, 4=depleted
  +
"""
  +
if "LB" in status_str:
  +
return 3
  +
if "OB" in status_str or "OL" in status_str:
  +
return 2
  +
return 1
  +
  +
  +
def value_for_oid(oid: str):
  +
"""Вернуть (type, value) или (None, None), если OID не знаем."""
  +
nut = get_nut()
  +
logger.debug(f"[value_for_oid] Data from NUT: {nut}")
  +
logger.debug(f"[value_for_oid] oid: {oid}")
  +
# По умолчанию
  +
OID = OIDS.get(oid)
  +
logger.debug(f"OID object: {OID}")
  +
if OID:
  +
logger.debug(f"[value_for_oid] OID = {OID.get('name')}: {oid}")
  +
get_method = OID.get("get_oid_method", get_oid_data)
  +
  +
try:
  +
oid_value = get_method(nut, OID.get("nut_key"))
  +
except TypeError:
  +
oid_value = get_method()
  +
  +
oid_type = OID.get("type", "integer")
  +
custom_multiplier = OID.get("custom_multiplier", 1)
  +
logger.debug(f"[value_for_oid] custom_multiplier: {custom_multiplier}")
  +
if custom_multiplier != 1:
  +
try:
  +
oid_value_digit = float(oid_value)
  +
oid_value = oid_value_digit * custom_multiplier
  +
except Exception as e:
  +
logger.debug(f"[value_for_oid] Error: {e}, ignoring. Original value: {oid_value}")
  +
logger.debug(f"OID Data: {oid} {oid_type} {oid_value}")
  +
return (oid_type, str(oid_value))
  +
  +
logger.debug(f"[value_for_oid] OID is not in the list of known OIDs")
  +
return (None, None)
  +
  +
  +
def handle_get(oid: str):
  +
logger.debug(f"handle GET: oid: {oid}")
  +
t, v = value_for_oid(oid)
  +
if t is None:
  +
logger.debug(f"handle GET: type: None")
  +
# Ничего не печатаем -> NoSuchInstance
  +
return
  +
  +
logger.debug(f"handle GET: oid: {oid} type: {t} value: {v}")
  +
logger.debug("Data printed to stdout 1")
  +
  +
print(f".{oid}")
  +
print(f"{t}")
  +
print(f"{v}")
  +
logger.debug("Data printed to stdout")
  +
  +
  +
  +
  +
def handle_getnext(request_oid: str):
  +
# Преобразовать строковый OID в tuple
  +
req_key = oid_key(request_oid)
  +
logger.debug(f"[handle_getnext] {request_oid} {req_key}")
  +
  +
# Сортировка по ключу tuple
  +
SORTED_OIDS = sorted(OIDS.keys(), key=oid_key)
  +
  +
# сначала пробуем найти точное совпадение
  +
try:
  +
# так как ищем след от текущего то находим индекс текущего
  +
# если он не последний -- if idx + 1 < len(SORTED_OIDS)
  +
idx = SORTED_OIDS.index(request_oid)
  +
logger.debug(f"[handle_getnext] {request_oid} has index {idx}")
  +
next_oid = SORTED_OIDS[idx + 1] if idx + 1 < len(SORTED_OIDS) else None
  +
logger.debug(f"[handle_getnext] Next OID {next_oid}")
  +
except ValueError:
  +
# если точного совпадения нет — ищем первый > request_oid
  +
next_oid = None
  +
for candidate in SORTED_OIDS:
  +
#
  +
if oid_key(candidate) > req_key:
  +
next_oid = candidate
  +
logger.debug(f"[handle_getnext] Next OID {next_oid}")
  +
break
  +
  +
if not next_oid:
  +
logger.debug(f"[handle_getnext] Next OID ont found")
  +
return # NoSuchInstance (ничего не печатаем)
  +
  +
oid_type, oid_value = value_for_oid(next_oid)
  +
  +
logger.debug(f"handle GET: oid: {next_oid} type: {oid_type} value: {oid_value}")
  +
logger.debug("Data printed to stdout")
  +
print(f".{next_oid}")
  +
print(f"{oid_type}")
  +
print(f"{oid_value}")
  +
logger.debug("Data printed to stdout")
  +
  +
  +
def main():
  +
logger.debug(f"sys.argv: {sys.argv}")
  +
if len(sys.argv) < 3:
  +
logger.debug(f"sys.argv should be 3 arguments, nothing to do, exiting")
  +
return
  +
  +
cmd = sys.argv[1]
  +
# Remove leading dot if any
  +
oid = sys.argv[2]
  +
oid = oid.lstrip('.')
  +
logger.debug(f"cmd: {cmd}. OID: {oid}")
  +
  +
if cmd == "-g":
  +
logger.debug("Handle get")
  +
handle_get(oid)
  +
elif cmd == "-n":
  +
logger.debug("Handle get next")
  +
handle_getnext(oid)
  +
# SET не поддерживаем, ничего не печатаем
  +
else:
  +
logger.debug("Handle is not configured")
  +
  +
if __name__ == "__main__":
  +
logger.info("Start")
  +
main()
 
</PRE>
 
</PRE>
 
}}
 
}}

Текущая версия на 10:41, 22 января 2026

Вольтроник

Внешний вид

Voltrronic 2E VP.JPG

Инструкция


Файл:Axpert V PF1 manual.pdf

Снимать данные с линукса

mpp-solar -p /dev/hidraw0 -c QPIGS   -I -P PI30

но работает лучше как минимум у меня через ком-порт и /dev/ttyUSB0
Но для этого нужен переходник USB -> COM
Если переходников несколько то лучше указывать by-id - /dev/serial/by-id/usb-1a86_USB2.0-Ser_-if00-port0

NUT

так как я хотел что бы инвертор прикинулся нормальным UPS а не вот это вот все то набросал простой скрипт для снятия данных (драйвер встроенный в NUT не заработал, написать свой на основе скрипта я не осилил)
Весь код написан на скорую руку, с кучей хардкода, так как надо было прям сейчас, а передывать пока нет времени
Логика работы такая:

  • скрипт mpp_nut_bridge.py (через systemd unit) работает в вечном цикле и складывает результат в файл /var/lib/nut/mpp.state
  • NUT умеет читать данные из внешнего файла через driver = dummy-ups
[mpp]
    driver = dummy-ups
    port = /var/lib/nut/mpp.state
    desc = "MPP via mppsolar"
  • для того что бы отдавать данные на zabbix используется бридж в SNMP

mpp_nut_bridge.py

mpp-nut-bridge.service

snmpd.conf

Часть конфига ответвенная за "проброс" запросов к snmpd
Тут три ветки

  • .1.3.6.1.2.1.33 "Стандартный" MIB - просто для теста, у меня в заббиксе он не используется
  • .1.3.6.1.4.1.318 MIB специфичный для APC - все части взяты из конфига темплейта zabbix, возможно в оригинальном MIB больше данных
  • .1.3.6.1.4.1.418 - ветка выбрана от фонаря и используется для мониторинга BMS батареи (это отдельная задача - мониторинг батареи Daly BMS)
  • ups-snmp-passpersist.py имя файла который дергать на запрос (ему передается тип изапроса GET/GETNEXT и OID, другие типы игнорируем)
pass .1.3.6.1.2.1.33 /etc/nut/ups-snmp-passpersist.py
pass .1.3.6.1.4.1.318 /etc/nut/ups-snmp-passpersist.py
pass .1.3.6.1.4.1.418 /etc/nut/batt-dale-snmp.py

По сути этот конфиг на каждый запрос вызывает скрипт с параметрами (хорошо бы конечно сделать постоянно висящего демона что бы не форкать на каждый запрос но пока и так сойдет)


ups-snmp-passpersist.py