Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Analog inputs and add sensors, digital inputs, and analog inputs to configuration #24

Merged
merged 10 commits into from
Nov 30, 2023
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ repos:
- id: pyupgrade
args: ["--py3-plus"]
- repo: https://github.com/pycqa/isort
rev: 5.11.4
rev: 5.12.0
hooks:
- id: isort
name: isort (python)
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,25 @@ example config file for the `WebRelayPreselector` to describe how it works:
"noise diode powered" : "relay2=1",
"antenna path enabled": "relay1=0",
"noise diode path enabled": "relay1=1"
},
"sensors": {
"internal_temp": 1,
"internal_humidity": 2,
"tec_intake_temp": 3,
"tec_exhaust_temp": 4
},
"digital_inputs": {
"ups_power": 1,
"ups_battery_level": 2,
"ups_trouble": 3,
"ups_battery_replace": 4
},
"analog_inputs": {
"door_sensor": 1,
"5vdc_monitor": 2,
"28vdc_monitor": 3,
"15vdc_monitor": 4,
"24vdc_monitor": 5
}
}
```
Expand All @@ -88,6 +107,11 @@ those specified in the mapping. Each of the entries in the config provide mappin
associated web relay input states and every RFPath defined in the sensor definition json
file should have an entry in the preselector config. The keys in the dictionary may use the
name of the RFPath or the index of the RFPath in the RFPaths array.
The `sensors`, `digital_inputs`, and `analog_inputs` keys define the sensors,
aromanielloNTIA marked this conversation as resolved.
Show resolved Hide resolved
digital_inputs and analog_inputs configured on the device. Within each of the sections,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend checking this paragraph for consistent usage of monospace formatting

each key provides the name of the sensor or input and the value specifies the assigned
sensor or input number. The get_satus method will provide each sensor/input value with
the specified label.

In this example, there are `noise_diode_on` and `noise_diode_off` keys to correspond to the
preselector paths to turn the noise diode on and off, and an antenna key to indicate the
Expand Down
19 changes: 19 additions & 0 deletions config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,25 @@
"noise diode powered" : "relay2=1",
"antenna path enabled": "relay1=0",
"noise diode path enabled": "relay1=1"
},
"sensors": {
"internal_temp": 1,
"internal_humidity": 2,
"tec_intake_temp": 3,
"tec_exhaust_temp": 4
},
"digital_inputs": {
"ups_power": 1,
"ups_battery_level": 2,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend changing "ups_battery_level" to a better descriptor like "ups_battery_low"

Also update the example in the README for any change made.

"ups_trouble": 3,
"ups_battery_replace": 4
},
"analog_inputs": {
"door_sensor": 1,
aromanielloNTIA marked this conversation as resolved.
Show resolved Hide resolved
"5vdc_monitor": 2,
"28vdc_monitor": 3,
"15vdc_monitor": 4,
"24vdc_monitor": 5
}

}
2 changes: 1 addition & 1 deletion src/its_preselector/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "3.0.2"
__version__ = "3.1.0"
38 changes: 38 additions & 0 deletions src/its_preselector/controlbyweb_web_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,23 @@ def get_digital_input_value(self, input_num: int) -> bool:
raise ConfigurationException(f"Digital Input {input_num} does not exist.")
return bool(int(digital_input.text))

def get_analog_input_value(self, input_num: int) -> bool:
aromanielloNTIA marked this conversation as resolved.
Show resolved Hide resolved
"""
Read float value from an analog input of the WebRelay.

:param input_num: Configured index of the desired analog input.
:raises ConfigurationException: If the requested analog input cannot be read.
:return: The desired analog input value.
"""
input_string = str(input_num)
response = self.get_state_xml()
input_tag = f"analogInput{input_string}"
aromanielloNTIA marked this conversation as resolved.
Show resolved Hide resolved
root = ET.fromstring(response.text)
sensor = root.find(input_tag)
if sensor is None:
raise ConfigurationException(f"Analog input {input_tag} does not exist.")
return float(sensor.text)

def set_state(self, key):
"""
Set the state of the relay.
Expand Down Expand Up @@ -143,6 +160,27 @@ def get_status(self):
for relay_state in relay_states:
matches = matches and self.state_matches(relay_state, xml_root)
state[key] = matches

if "sensors" in self.config:
for key, value in self.config["sensors"].items():
try:
state[key] = self.get_sensor_value(value)
aromanielloNTIA marked this conversation as resolved.
Show resolved Hide resolved
except:
logger.error(
f"Unable to get sensor value for sensor:{value}"
)
if "digital_inputs" in self.config:
for key, value in self.config["digital_inputs"].items():
try:
state[key] = self.get_digital_input_value(value)
except:
logger.error(f"Unable to read digital input:{value}")
if "analog_inputs" in self.config:
for key, value in self.config["analog_inputs"].items():
try:
state[key] = self.get_analog_input_value(value)
except:
logger.error(f"Unable to read analog input:{value}")
except:
logger.error("Unable to get status")
state["healthy"] = healthy
Expand Down
9 changes: 9 additions & 0 deletions src/its_preselector/web_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ def get_digital_input_value(input) -> bool:
:return: The boolean value read from the digital input.
"""

@abstractmethod
def get_analog_input_value(input) -> float:
"""
Read the value from an analog input on the web relay.

:param input: The name or ID of the analog input
aromanielloNTIA marked this conversation as resolved.
Show resolved Hide resolved
:return: The float value read from the analog input.
"""

@abstractmethod
def set_state(self, state_key: str) -> None:
"""
Expand Down
27 changes: 27 additions & 0 deletions tests/test_controlbyweb_web_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def setUpClass(cls):
"<relay4>0</relay4>"
"<vin>27.6</vin>"
"<register1>0</register1>"
"<analogInput1>1.4</analogInput1>"
"<oneWireSensor1>102.3</oneWireSensor1>"
"<utcTime>9160590</utcTime>"
"<timezoneOffset>-25200</timezoneOffset>"
Expand Down Expand Up @@ -87,6 +88,32 @@ def test_get_state_from_config(self):
self.assertTrue(states["noise diode path enabled"])
self.assertTrue(states["noise on"])

def test_get_analog_input(self):
aromanielloNTIA marked this conversation as resolved.
Show resolved Hide resolved
root = ET.fromstring(self.state)
web_relay = ControlByWebWebRelay(
{
"base_url": "http://127.0.0.1",
"name": "test_preselector",
"control_states": {
"noise_diode_off": "1State=1,2State=0,3State=0,4State=0"
},
"status_states": {
"noise diode powered": "relay2=1",
"antenna path enabled": "relay1=0",
"noise diode path enabled": "relay1=1",
"noise on": "relay2=1,relay1=1",
"measurements": "relay1=0,relay2=0,relay3=0,relay4=0",
},
"analog_inputs": {"analogInputTest": 1},
}
)
response = Response()
response.status_code = codes.ok
type(response).text = PropertyMock(return_value=self.state)
web_relay.get_state_xml = MagicMock(return_value=response)
analogInputVal = web_relay.get_analog_input_value(1)
self.assertEqual(1.4, analogInputVal)

def test_get_status(self):
web_relay = ControlByWebWebRelay(
{
Expand Down