From 0e8197fd2d2f040f1d23fedf20ed78cb813d74ce Mon Sep 17 00:00:00 2001 From: reachableceo Date: Wed, 2 Sep 2026 19:18:43 -0500 Subject: [PATCH] =?UTF-8?q?refactor(plant):=20two-layer=20snmp=20sensors?= =?UTF-8?q?=20=E2=80=94=20raw=20transport=20+=20semantic=20templates=20[#3?= =?UTF-8?q?44]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HA 2026.8 rejects ANY non-numeric native_value on a unit-bearing snmp sensor at entity ADD (host down at boot renders ''/'unknown'/'None' as strings) — unit-bearing adds crash and the entity vanishes until manual reload. Raw layer now carries no unit (validation off); semantic template layer carries unit + device_class under the original unique_ids so entity_ids, dashboards, and automations are unchanged. https://projects.knownelement.com/issues/344#note-3916 --- packages/plant_snmp.yaml | 364 +++++++++++++++++++++++++-------------- 1 file changed, 239 insertions(+), 125 deletions(-) diff --git a/packages/plant_snmp.yaml b/packages/plant_snmp.yaml index 520038d..21df8ca 100644 --- a/packages/plant_snmp.yaml +++ b/packages/plant_snmp.yaml @@ -1,326 +1,286 @@ sensor: - # NOTE (2026-09-02, HA 2026.8): NO device_class on snmp platform sensors. - # HA 2026.8 hard-fails entity ADD when a device_classed sensor's first - # render is empty (host down/asleep at boot) — ValueError int('') drops - # the entity until manual reload. unit_of_measurement alone keeps °F/W/A - # display; numeric_state triggers and dashboards do not need device_class. - # --- TEMPer USB probe hosts (External probe, Internal fallback) --- + # ARCHITECTURE (2026-09-02, HA 2026.8) — two layers, do not re-merge: + # RAW layer (platform snmp, below): transport only. NO unit_of_measurement + # and NO device_class — HA 2026.8 raises ValueError at entity ADD when a + # unit-bearing sensor's native_value is non-numeric (host down/asleep at + # boot renders '' or the literal string 'unknown'; even {{ none }} is + # stringified to 'None'). Unit-bearing adds CRASH and the entity vanishes. + # SEMANTIC layer (template: below): carries unit + device_class under the + # ORIGINAL unique_ids (registry keeps the original entity_ids so dashboards + # and automations are unaffected). Template platform degrades to unknown + # gracefully. value must never render empty — guard with is_number(). + # --- RAW SNMP transport (lm-sensors / drac / pdu extends) --- - platform: snmp - name: "PFV artroom temperature" + name: "PFV artroom temperature raw" host: stlpc-artroom.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - default_value: "-999" - unit_of_measurement: "°F" - unique_id: pfv_artroom_temperature + unique_id: pfv_artroom_temperature_raw value_template: >- {% set ext = value | regex_findall('External:\s*\+?(-?\d+\.?\d*)') %} {% set int = value | regex_findall('Internal:\s*\+?(-?\d+\.?\d*)') %} {% set c = (ext | first) if ext else ((int | first) if int else none) %} - {{ ((c | float * 9 / 5) + 32) | round(1) if c is not none else none }} + {{ c | float if c is not none }} - platform: snmp - name: "PFV garage temperature" + name: "PFV garage temperature raw" host: stlpc-garage.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - default_value: "-999" - unit_of_measurement: "°F" - unique_id: pfv_garage_temperature + unique_id: pfv_garage_temperature_raw value_template: >- {% set ext = value | regex_findall('External:\s*\+?(-?\d+\.?\d*)') %} {% set int = value | regex_findall('Internal:\s*\+?(-?\d+\.?\d*)') %} {% set c = (ext | first) if ext else ((int | first) if int else none) %} - {{ ((c | float * 9 / 5) + 32) | round(1) if c is not none else none }} + {{ c | float if c is not none }} - platform: snmp - name: "PFV tsys1 rack temperature" + name: "PFV tsys1 rack temperature raw" host: pfv-tsys1.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys1_rack_temperature + unique_id: pfv_tsys1_rack_temperature_raw value_template: >- {% set ext = value | regex_findall('External:\s*\+?(-?\d+\.?\d*)') %} {% set int = value | regex_findall('Internal:\s*\+?(-?\d+\.?\d*)') %} {% set c = (ext | first) if ext else ((int | first) if int else none) %} - {{ ((c | float * 9 / 5) + 32) | round(1) if c is not none else none }} + {{ c | float if c is not none }} - # --- Native lm-sensors hosts (label picked per host ground truth 2026-08-27) --- - # tsys3 Precision 7510: CPU package - platform: snmp - name: "PFV tsys3 CPU temperature" + name: "PFV tsys3 CPU temperature raw" host: pfv-tsys3.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys3_cpu_temperature + unique_id: pfv_tsys3_cpu_temperature_raw value_template: >- {% set m = value | regex_findall('Package id 0:\s*\+?(-?\d+\.?\d*)') %} - {{ ((m | first | float * 9 / 5) + 32) | round(1) if m }} + {{ m | first | float if m }} - # tsys4 Precision T1700: Dell SMM ambient (board inlet) - platform: snmp - name: "PFV tsys4 ambient temperature" + name: "PFV tsys4 ambient temperature raw" host: pfv-tsys4.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys4_ambient_temperature + unique_id: pfv_tsys4_ambient_temperature_raw value_template: >- {% set m = value | regex_findall('Ambient:\s*\+?(-?\d+\.?\d*)') %} - {{ ((m | first | float * 9 / 5) + 32) | round(1) if m }} + {{ m | first | float if m }} - # tsys5 Precision T7500: Dell SMM ambient (board inlet) - platform: snmp - name: "PFV tsys5 ambient temperature" + name: "PFV tsys5 ambient temperature raw" host: pfv-tsys5.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys5_ambient_temperature + unique_id: pfv_tsys5_ambient_temperature_raw value_template: >- {% set m = value | regex_findall('Ambient:\s*\+?(-?\d+\.?\d*)') %} - {{ ((m | first | float * 9 / 5) + 32) | round(1) if m }} + {{ m | first | float if m }} - # tsys6 PowerEdge R610: socket-0 core temp (no Package label on Westmere) - platform: snmp - name: "PFV tsys6 CPU temperature" + name: "PFV tsys6 CPU temperature raw" host: pfv-tsys6.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys6_cpu_temperature + unique_id: pfv_tsys6_cpu_temperature_raw value_template: >- {% set m = value | regex_findall('Core 0:\s*\+?(-?\d+\.?\d*)') %} - {{ ((m | first | float * 9 / 5) + 32) | round(1) if m }} + {{ m | first | float if m }} - # tsys6 ACPI power meter (node draw, watts) - platform: snmp - name: "PFV tsys6 node power" + name: "PFV tsys6 node power raw" host: pfv-tsys6.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "W" - unique_id: pfv_tsys6_node_power + unique_id: pfv_tsys6_node_power_raw value_template: >- {% set m = value | regex_findall('power1:\s*(\d+\.?\d*)\s*W') %} {{ m | first | float if m }} - # tsys7 PowerEdge R620: package temp - platform: snmp - name: "PFV tsys7 CPU temperature" + name: "PFV tsys7 CPU temperature raw" host: pfv-tsys7.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys7_cpu_temperature + unique_id: pfv_tsys7_cpu_temperature_raw value_template: >- {% set m = value | regex_findall('Package id 0:\s*\+?(-?\d+\.?\d*)') %} - {{ ((m | first | float * 9 / 5) + 32) | round(1) if m }} + {{ m | first | float if m }} - # tsys7 ACPI power meter (node draw, watts) - platform: snmp - name: "PFV tsys7 node power" + name: "PFV tsys7 node power raw" host: pfv-tsys7.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "W" - unique_id: pfv_tsys7_node_power + unique_id: pfv_tsys7_node_power_raw value_template: >- {% set m = value | regex_findall('power1:\s*(\d+\.?\d*)\s*W') %} {{ m | first | float if m }} - # tsys9 OptiPlex 7080: package temp - platform: snmp - name: "PFV tsys9 CPU temperature" + name: "PFV tsys9 CPU temperature raw" host: pfv-tsys9.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys9_cpu_temperature + unique_id: pfv_tsys9_cpu_temperature_raw value_template: >- {% set m = value | regex_findall('Package id 0:\s*\+?(-?\d+\.?\d*)') %} - {{ ((m | first | float * 9 / 5) + 32) | round(1) if m }} + {{ m | first | float if m }} - # --- SBC fleet (sysfs thermal fallback in lmsensors-extend v2; temp1 = zone0/SoC) --- - platform: snmp - name: "PFV jetson temperature" + name: "PFV jetson temperature raw" host: pfv-jetson-nano-1.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_jetson_temperature + unique_id: pfv_jetson_temperature_raw value_template: >- {% set m = value | regex_findall('temp1:\s*\+?(-?\d+\.?\d*)') %} - {{ ((m | first | float * 9 / 5) + 32) | round(1) if m }} + {{ m | first | float if m }} - platform: snmp - name: "PFV svrpi temperature" + name: "PFV svrpi temperature raw" host: pfvsvrpi.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_svrpi_temperature + unique_id: pfv_svrpi_temperature_raw value_template: >- {% set m = value | regex_findall('temp1:\s*\+?(-?\d+\.?\d*)') %} - {{ ((m | first | float * 9 / 5) + 32) | round(1) if m }} + {{ m | first | float if m }} - platform: snmp - name: "PFV subopi3 temperature" + name: "PFV subopi3 temperature raw" host: subopi3.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - default_value: "-999" - unit_of_measurement: "°F" - unique_id: pfv_subopi3_temperature + unique_id: pfv_subopi3_temperature_raw value_template: >- {% set m = value | regex_findall('temp1:\s*\+?(-?\d+\.?\d*)') %} - {{ ((m | first | float * 9 / 5) + 32) | round(1) if m else none }} + {{ m | first | float if m }} - platform: snmp - name: "PFV subopi-dev-3 temperature" + name: "PFV subopi-dev-3 temperature raw" host: subopi-dev-3.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_subopi_dev_3_temperature + unique_id: pfv_subopi_dev_3_temperature_raw value_template: >- {% set m = value | regex_findall('temp1:\s*\+?(-?\d+\.?\d*)') %} - {{ ((m | first | float * 9 / 5) + 32) | round(1) if m }} + {{ m | first | float if m }} - platform: snmp - name: "PFV subopi-dev-4 temperature" + name: "PFV subopi-dev-4 temperature raw" host: subopi-dev-4.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_subopi_dev_4_temperature + unique_id: pfv_subopi_dev_4_temperature_raw value_template: >- {% set m = value | regex_findall('temp1:\s*\+?(-?\d+\.?\d*)') %} - {{ ((m | first | float * 9 / 5) + 32) | round(1) if m }} + {{ m | first | float if m }} - - # --- iDRAC IPMI temps via drac-extend (SNMP MIB tables empty on this hw gen) [#625] --- - # Deployed 2026-09-02: /usr/local/bin/drac-extend + snmpd "drac" extend on tsys6/tsys7. - platform: snmp - name: "PFV tsys6 DRAC ambient" + name: "PFV tsys6 DRAC ambient raw" host: pfv-tsys6.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.4.100.114.97.99 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys6_drac_ambient + unique_id: pfv_tsys6_drac_ambient_raw value_template: >- {% set m = value | regex_findall('ambient_temp (\d+)') %} - {{ ((m[0] | float * 9 / 5) + 32) | round(1) if m | length > 0 }} + {{ m[0] | float if m | length > 0 }} - platform: snmp - name: "PFV tsys7 DRAC inlet" + name: "PFV tsys7 DRAC inlet raw" host: pfv-tsys7.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.4.100.114.97.99 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys7_drac_inlet + unique_id: pfv_tsys7_drac_inlet_raw value_template: >- {% set m = value | regex_findall('inlet_temp (\d+)') %} - {{ ((m[0] | float * 9 / 5) + 32) | round(1) if m | length > 0 }} + {{ m[0] | float if m | length > 0 }} - platform: snmp - name: "PFV tsys7 DRAC exhaust" + name: "PFV tsys7 DRAC exhaust raw" host: pfv-tsys7.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.4.100.114.97.99 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys7_drac_exhaust + unique_id: pfv_tsys7_drac_exhaust_raw value_template: >- {% set m = value | regex_findall('exhaust_temp (\d+)') %} - {{ ((m[0] | float * 9 / 5) + 32) | round(1) if m | length > 0 }} + {{ m[0] | float if m | length > 0 }} - platform: snmp - name: "PFV tsys7 DRAC CPU1" + name: "PFV tsys7 DRAC CPU1 raw" host: pfv-tsys7.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.4.100.114.97.99 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys7_drac_cpu1 + unique_id: pfv_tsys7_drac_cpu1_raw value_template: >- {% set t = value | regex_findall('(?:^|\n)temp (\d+)') %} - {{ ((t[0] | float * 9 / 5) + 32) | round(1) if t | length > 0 }} + {{ t[0] | float if t | length > 0 }} - platform: snmp - name: "PFV tsys7 DRAC CPU2" + name: "PFV tsys7 DRAC CPU2 raw" host: pfv-tsys7.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.4.100.114.97.99 accept_errors: true - unit_of_measurement: "°F" - unique_id: pfv_tsys7_drac_cpu2 + unique_id: pfv_tsys7_drac_cpu2_raw value_template: >- {% set t = value | regex_findall('(?:^|\n)temp (\d+)') %} - {{ ((t[1] | float * 9 / 5) + 32) | round(1) if t | length > 1 }} + {{ t[1] | float if t | length > 1 }} - # --- Garage PDU (APC AP7830 metered) via tsys-librenms relay [#344] --- - # Relay: old PDU speaks SNMPv1 only; tsys-librenms is the sanctioned vantage - # (dcinfra/sensors/pdu/garage-pdu-relay.sh in PFVCluster). - platform: snmp - name: "PFV garage PDU output current" + name: "PFV garage PDU output current raw" host: tsys-librenms.knel.net version: 2c community: kn3lmgmt baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.103.97.114.97.103.101.112.100.117 accept_errors: true - unit_of_measurement: "A" - unique_id: pfv_garage_pdu_amps + unique_id: pfv_garage_pdu_amps_raw value_template: >- {% set m = value | regex_findall('output_amps ([0-9.]+)') %} - {{ m[0] if m | length > 0 }} + {{ m[0] | float if m | length > 0 }} # --- Garage PDU energy (kWh) integrated from watts [#344] --- - # NOTE: lives in THIS sensor: block on purpose — a second top-level - # "sensor:" key anywhere in a package shadows the whole first block - # (YAML duplicate keys are last-one-wins) and silently kills every - # SNMP sensor at next HA restart. Never append a second sensor: key. - platform: integration source: sensor.pfv_garage_pdu_power name: pfv garage PDU energy @@ -330,16 +290,6 @@ sensor: method: left unique_id: pfv_garage_pdu_energy -template: - - sensor: - - name: "PFV garage PDU power" - unique_id: pfv_garage_pdu_watts - device_class: power - unit_of_measurement: "W" - state: >- - {% set a = states('sensor.pfv_garage_pdu_output_current') | float(-1) %} - {{ (a * 120) | round(0) if a >= 0 }} - # --- Rate-of-change early warning (#682): +10°F/15min = AC trouble [#344] --- binary_sensor: - platform: trend @@ -371,3 +321,167 @@ utility_meter: name: Garage PDU daily energy unique_id: garage_pdu_daily_energy cycle: daily + +template: + - sensor: + - name: "PFV artroom temperature" + unique_id: pfv_artroom_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_artroom_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV garage temperature" + unique_id: pfv_garage_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_garage_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys1 rack temperature" + unique_id: pfv_tsys1_rack_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys1_rack_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys3 CPU temperature" + unique_id: pfv_tsys3_cpu_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys3_cpu_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys4 ambient temperature" + unique_id: pfv_tsys4_ambient_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys4_ambient_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys5 ambient temperature" + unique_id: pfv_tsys5_ambient_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys5_ambient_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys6 CPU temperature" + unique_id: pfv_tsys6_cpu_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys6_cpu_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys6 node power" + unique_id: pfv_tsys6_node_power + device_class: power + unit_of_measurement: "W" + state: >- + {% set v = states('sensor.pfv_tsys6_node_power_raw') %} + {{ v | float | round(1) if is_number(v) else none }} + - name: "PFV tsys7 CPU temperature" + unique_id: pfv_tsys7_cpu_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys7_cpu_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys7 node power" + unique_id: pfv_tsys7_node_power + device_class: power + unit_of_measurement: "W" + state: >- + {% set v = states('sensor.pfv_tsys7_node_power_raw') %} + {{ v | float | round(1) if is_number(v) else none }} + - name: "PFV tsys9 CPU temperature" + unique_id: pfv_tsys9_cpu_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys9_cpu_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV jetson temperature" + unique_id: pfv_jetson_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_jetson_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV svrpi temperature" + unique_id: pfv_svrpi_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_svrpi_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV subopi3 temperature" + unique_id: pfv_subopi3_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_subopi3_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV subopi-dev-3 temperature" + unique_id: pfv_subopi_dev_3_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_subopi_dev_3_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV subopi-dev-4 temperature" + unique_id: pfv_subopi_dev_4_temperature + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_subopi_dev_4_temperature_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys6 DRAC ambient" + unique_id: pfv_tsys6_drac_ambient + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys6_drac_ambient_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys7 DRAC inlet" + unique_id: pfv_tsys7_drac_inlet + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys7_drac_inlet_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys7 DRAC exhaust" + unique_id: pfv_tsys7_drac_exhaust + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys7_drac_exhaust_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys7 DRAC CPU1" + unique_id: pfv_tsys7_drac_cpu1 + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys7_drac_cpu1_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV tsys7 DRAC CPU2" + unique_id: pfv_tsys7_drac_cpu2 + device_class: temperature + unit_of_measurement: "°F" + state: >- + {% set v = states('sensor.pfv_tsys7_drac_cpu2_raw') %} + {{ ((v | float * 9 / 5) + 32) | round(1) if is_number(v) else none }} + - name: "PFV garage PDU output current" + unique_id: pfv_garage_pdu_amps + device_class: current + unit_of_measurement: "A" + state: >- + {% set v = states('sensor.pfv_garage_pdu_amps_raw') %} + {{ v | float | round(1) if is_number(v) else none }} + - name: "PFV garage PDU power" + unique_id: pfv_garage_pdu_watts + device_class: power + unit_of_measurement: "W" + state: >- + {% set a = states('sensor.pfv_garage_pdu_output_current') | float(-1) %} + {{ (a * 120) | round(0) if a >= 0 }}