#!/usr/bin/python3
# SPDX-License-Identifier: BSD-3-Clause
#
# Copyright 2022 Raritan Inc. All rights reserved.

import copy, sys, os
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/pdu-python-api")
from raritan.rpc import Agent, sensors
from raritan.rpc.peripheral import DeviceManager, GatewaySensorManager, ModbusCfg

ip = "10.0.42.2"
user = "admin"
pw = "raritan"

try:
    ip = sys.argv[1]
    user = sys.argv[2]
    pw = sys.argv[3]
except IndexError:
    pass # use defaults

print(f"Authenticate to {ip} with user {user} and pw {pw}")

agent = Agent("https", ip, user, pw, disable_certificate_verification=True)
mgr = DeviceManager("/model/peripheraldevicemanager", agent)
gwcfg = mgr.getGatewaySensorManager()

classVoltage = GatewaySensorManager.NumericSensorClass(
    classId = "Voltage",
    metadata = sensors.NumericSensor.MetaData(
        type = sensors.Sensor.TypeSpec(
            sensors.Sensor.NUMERIC,
            sensors.Sensor.VOLTAGE,
            sensors.Sensor.VOLT
        ),
        decdigits = 1,
        accuracy = 0.0,
        resolution = 1.0,
        tolerance = 0,
        noiseThreshold = 1,
        range = sensors.NumericSensor.Range(0, 32767),
        thresholdCaps = sensors.NumericSensor.ThresholdCapabilities(True, True, True, True),
    ),
    defaultThresholds = sensors.NumericSensor.Thresholds(
        upperCriticalActive = True,
        upperCritical = 254.0,
        upperWarningActive = True,
        upperWarning = 247.0,
        lowerWarningActive = True,
        lowerWarning = 194.0,
        lowerCriticalActive = True,
        lowerCritical = 188.0,
        assertionTimeout = 0,
        deassertionHysteresis = 2.0
    ),
    preferCommonThresholds = False
)

stateClass = GatewaySensorManager.StateSensorClass(
    classId = "State",
    type = sensors.Sensor.TypeSpec(
        sensors.Sensor.DISCRETE_ON_OFF,
        sensors.Sensor.CONTACT_CLOSURE,
        sensors.Sensor.NONE
    )
)

switchClass = GatewaySensorManager.SwitchSensorClass(
    classId = "Switch",
    type = sensors.Sensor.TypeSpec(
        sensors.Sensor.DISCRETE_ON_OFF,
        sensors.Sensor.CONTACT_CLOSURE,
        sensors.Sensor.NONE
    )
)

remoteDeviceV1V2 = GatewaySensorManager.RemoteSnmpV1V2Device(
    deviceId = "SNMPV2",
    disabled = False,
    name = "Localhost",
    timeoutMs = 100,
    host = "127.0.0.1",
    community = "private"
)

remoteDeviceV3 = GatewaySensorManager.RemoteSnmpV3Device(
    deviceId = "SNMPV3",
    disabled = False,
    name = "Localhost",
    timeoutMs = 100,
    host = "127.0.0.1",
    authProtocol = GatewaySensorManager.SnmpAuthProtocol.SHA1,
    authPassphrase = "legrand1",
    level = GatewaySensorManager.SnmpSecurityLevel.AUTH_PRIV,
    user = "admin",
    privacyProtocol = GatewaySensorManager.SnmpPrivProtocol.AES128,
    privacyPassphrase = "legrand1"
)

gaugeEnc = GatewaySensorManager.NumericValueEncoding(
    encodingId = "GAUGE",
    type = GatewaySensorManager.EncodingType.UINT,
)

boolEnc = GatewaySensorManager.NumericValueEncoding(
    encodingId = "BOOL",
    type = GatewaySensorManager.EncodingType.BOOL,
)
boolEncEnum = GatewaySensorManager.NumericValueEncoding(
    encodingId = "BOOL/enum",
    type = GatewaySensorManager.EncodingType.BOOL,
    interpretationRules = [
        GatewaySensorManager.InterpretationRuleEnum(
            interpretation = GatewaySensorManager.Interpretation.STATE_OFF,
            enumValues = [0]
        ),
        GatewaySensorManager.InterpretationRuleEnum(
            interpretation = GatewaySensorManager.Interpretation.STATE_ON,
            enumValues = [1]
        ),
    ]
)
boolEncRaw = GatewaySensorManager.NumericValueEncoding(
    encodingId = "BOOL/raw",
    type = GatewaySensorManager.EncodingType.BOOL,
    interpretationRules = [
        GatewaySensorManager.InterpretationRuleRAW(
            interpretation = GatewaySensorManager.Interpretation.STATE_OFF,
            value = 0,
            mask = 1,
        ),
        GatewaySensorManager.InterpretationRuleRAW(
            interpretation = GatewaySensorManager.Interpretation.STATE_ON,
            value = 1,
            mask = 1,
        ),
    ]
)

boolEncRangeRaw = GatewaySensorManager.NumericValueEncoding(
    encodingId = "BOOL/rangeraw",
    type = GatewaySensorManager.EncodingType.BOOL,
    interpretationRules = [
        GatewaySensorManager.InterpretationRuleRangeRAW(
            interpretation = GatewaySensorManager.Interpretation.STATE_OFF,
            min = 0,
            max = 0,
        ),
        GatewaySensorManager.InterpretationRuleRangeRAW(
            interpretation = GatewaySensorManager.Interpretation.STATE_ON,
            min = 1,
            max = 0xFFFFFFFF,
        ),
    ]
)

stateEnc = GatewaySensorManager.NumericValueEncoding(
    encodingId = "STATE",
    type = GatewaySensorManager.EncodingType.BOOL,
    interpretationRules = [
        GatewaySensorManager.InterpretationRuleEnum(
            interpretation = GatewaySensorManager.Interpretation.STATE_OFF,
            enumValues = [ 5, "7", "0xB" ] # above upper warning/on/alarmed
        ),
        GatewaySensorManager.InterpretationRuleEnum(
            interpretation = GatewaySensorManager.Interpretation.STATE_ON,
            enumValues = [ 0x8 ] # off
        ),
        GatewaySensorManager.InterpretationRuleCatchAll(
            interpretation = GatewaySensorManager.Interpretation.UNAVAILABLE,
        )
    ],
)

# https://cdn.raritan.com/download/PXE/v3.2.10/mib-usage.pdf
outletVoltageSensor1 = GatewaySensorManager.SnmpSensor(
    sensorId = "outletVoltage1",
    deviceId = "SNMPV2",
    classId = "Voltage",
    encodingId = "GAUGE",
    defaultName = "Outlet 1 Voltage",
    oid = "1.3.6.1.4.1.13742.6.5.4.3.1.4.1.1.4"
)

outletStateSensor1 = GatewaySensorManager.SnmpSensor(
    sensorId = "outletState1",
    deviceId = "SNMPV2",
    classId = "Switch",
    encodingId = "BOOL",
    defaultName = "Outlet 1 State",
    oid = "1.3.6.1.4.1.13742.6.4.1.2.1.2.1.1"
)

outletVoltageSensor2 = GatewaySensorManager.SnmpSensor(
    sensorId = "outletVoltage2",
    deviceId = "SNMPV3",
    classId = "Voltage",
    encodingId = "GAUGE",
    defaultName = "Outlet 2 Voltage",
    oid = "1.3.6.1.4.1.13742.6.5.4.3.1.4.1.2.4"
)

outletStateSensor2 = GatewaySensorManager.SnmpSensor(
    sensorId = "outletState2",
    deviceId = "SNMPV3",
    classId = "State",
    encodingId = "STATE",
    defaultName = "Outlet 2 State",
    oid = "1.3.6.1.4.1.13742.6.5.4.3.1.3.1.2.14"
)

cfg = gwcfg.getConfiguration()

cfg["SNMP-Localhost"] = GatewaySensorManager.ConfigurationPackage(
    name = "QEMU SNMP localhost",
    classes = [ classVoltage, switchClass, stateClass ],
    devices = [ remoteDeviceV1V2, remoteDeviceV3],
    encodings = [ gaugeEnc, boolEnc, boolEncEnum, boolEncRaw, boolEncRangeRaw, stateEnc ],
    sensors = [ outletVoltageSensor1, outletVoltageSensor2, outletStateSensor1, outletStateSensor2 ]
)

ret = gwcfg.setConfiguration(cfg)

match ret:
    case GatewaySensorManager.ERR_CONFIG_INCONSISTENT:
        print("FAILED: Consistency check has failed (e.g. non-unique identifiers)")

    case GatewaySensorManager.ERR_CONFIG_STORAGE_FAILED:
        print("FAILED: Configuration cannot be stored.")

    case _:
        print("Configuration stored")
