Skip to content

Commit

Permalink
Reworked bay_position MQTT topic, discovery, and data assembly to sen…
Browse files Browse the repository at this point in the history
…d as a JSON dictionary and to have HA pull it apart appropriately. Now sends 'adjusted reading' and 'raw reading'. Adjusted reading is the default, but raw reading is available in the HA interface.
  • Loading branch information
chrisgilldc committed Nov 21, 2022
1 parent f407741 commit 25ea88d
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 61 deletions.
82 changes: 26 additions & 56 deletions lib/bay.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,20 +201,23 @@ def _check_occupancy(self):
if self._quality[self._settings['detectors']['selected_range']] in ('No object', 'Door open'):
# If the detector can hit the garage door, or the door is open, then clearly nothing is in the way, so
# the bay is vacant.
self._logger.debug("Longitudinal quality is {}, not occupied.".format(self._quality[self._settings['detectors']['selected_range']]))
self._logger.debug("Longitudinal quality is {}, not occupied.".
format(self._quality[self._settings['detectors']['selected_range']]))
return False
if self._quality[self._settings['detectors']['selected_range']] in ('Emergency!', 'Back up', 'Park', 'Final', 'Base'):
# If the detector is giving us any of the 'close enough' qualities, there's something being found that
# could be a vehicle. Check the lateral sensors to be sure that's what it is, rather than somebody blocking
# the sensors or whatnot
self._logger.debug("Longitudinal quality is {}, could be occupied.".format(self._quality['longitudinal']))
self._logger.debug("Longitudinal quality is {}, could be occupied.".
format(self._quality[self._settings['detectors']['selected_range']]))
lat_score = 0
max_score = len(self._settings['detectors']['lateral'])
for detector in self._settings['detectors']['lateral']:
if self._quality['lateral'][detector] in ('OK', 'Warning', 'Critical'):
if self._quality[detector] in ('OK', 'Warning', 'Critical'):
# No matter how badly parked the vehicle is, it's still *there*
lat_score += 1
self._logger.debug("Achieved lateral score {} of {}".format(lat_score, len(self._quality['lateral'])))
if lat_score == len(self._settings['detectors']['lateral']):
self._logger.debug("Achieved lateral score {} of {}".format(lat_score, max_score))
if lat_score == max_score:
# All sensors have found something more or less in the right place, so yes, we're occupied!
return True
# If for some reason we drop through to here, assume we're not occupied.
Expand Down Expand Up @@ -276,11 +279,11 @@ def mqtt_messages(self, verify=False):
'message': self.occupied,
'repeat': True,
'topic_mappings': {'bay_id': self.bay_id}},
{'topic_type': 'bay',
'topic': 'bay_position',
'message': self.position,
'repeat': True,
'topic_mappings': {'bay_id': self.bay_id}},
# {'topic_type': 'bay',
# 'topic': 'bay_position',
# 'message': self.position,
# 'repeat': True,
# 'topic_mappings': {'bay_id': self.bay_id}},
{'topic_type': 'bay',
'topic': 'bay_quality',
'message': self.quality,
Expand All @@ -296,6 +299,19 @@ def mqtt_messages(self, verify=False):
'message': self._detectors[self._settings['detectors']['selected_range']].motion,
'repeat': True,
'topic_mappings': {'bay_id': self.bay_id}}]
# Positions for all the detectors.
for detector in self._detectors:
outbound_messages.append(
{'topic_type': 'bay',
'topic': 'bay_position',
'message': {
'adjusted_reading': self._detectors[detector].value,
'raw_reading': self._detectors[detector].value_raw
},
'repeat': False,
'topic_mappings': {'bay_id': self.bay_id, 'detector_id': self._detectors[detector].id }
}
)

if self._dock_timer['mark'] is None:
message = 'offline'
Expand Down Expand Up @@ -407,49 +423,3 @@ def _setup_detectors(self):
"Setting property {} to {}".format(item, self._settings['detectors']['settings'][dc][item]))
setattr(self._detectors[dc], item, self._settings['detectors']['settings'][dc][item])

# # Method to set up the detectors for the bay. This applies bay-specific options to the individual detectors, which
# # are initialized by the main routine.
# def _setup_detectors_old(self, detectors):
# # Initialize the detector dicts.
# self._detectors = {
# 'longitudinal': {},
# 'lateral': {}
# }
# lateral_order = {}
# config_options = {
# 'longitudinal': ('offset', 'bay_depth', 'spread_park'),
# 'lateral': ('offset', 'spread_ok', 'spread_warn', 'side', 'intercept')
# }
# for direction in self._detectors.keys():
# self._logger.debug("Checking for {} detectors.".format(direction))
# if direction in self._settings:
# self._logger.debug("Setting up {} detectors.".format(direction))
# for detector_config in self._settings[direction]['detectors']:
# self._logger.debug("Provided detector config: {}".format(detector_config))
# try:
# detector_obj = detectors[detector_config['detector']]
# except KeyError:
# raise KeyError("Tried to create lateral zone with detector '{}' but detector not defined."
# .format(detector_config['detector']))
# # Set the object attributes from the configuration.
# for item in config_options[direction]:
# try:
# setattr(detector_obj, item, detector_config[item])
# except KeyError:
# self._logger.debug("Using default value for {}".format(item))
# try:
# setattr(detector_obj, item, self._settings[direction]['defaults'][item])
# except KeyError:
# raise KeyError("Needed default value for {} but not defined!".format(item))
# # # If we're processing lateral, add the intercept range to the lateral order dict.
# if direction == 'lateral':
# lateral_order[detector_config['detector']] = Quantity(detector_config['intercept'])
# # Append the object to the appropriate object store.
# self._detectors[direction][detector_config['detector']] = detector_obj
# else:
# self._logger.debug("No detectors defined.")
# # Check for lateral order.
# if lateral_order is not None:
# self._logger.debug("Now have lateral order: {}".format(lateral_order))
# self._lateral_order = sorted(lateral_order, key=lateral_order.get)
# self._logger.debug("Sorted lateral order: {}".format(self._lateral_order))
15 changes: 15 additions & 0 deletions lib/detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,21 @@ def value(self):
else:
return "Error"

# Method to get the raw sensor reading. This is used to report upward for HA extended attributes.
@property
@read_if_stale
def value_raw(self):
self._logger.debug("Most recent reading is: {}".format(self._history[0][0]))
if isinstance(self._history[0][0], Quantity):
return self._history[0][0]
elif self._history[0][0] is None:
return "Unknown"
elif isinstance(self._history[0][0], str):
if self._history[0][0] == 'No reading':
return "No reading"
else:
return "Error"

# Assess the quality of the sensor
@property
@read_if_stale
Expand Down
23 changes: 18 additions & 5 deletions lib/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,16 +210,29 @@ def __init__(self, config):
}
},
# Adjusted readings from the sensors.
# 'bay_position': {
# 'topic': 'cobrabay/' + self._client_id + '/{0[bay_id]}/position',
# 'previous_state': None,
# 'ha_discovery': {
# 'name': '{0[bay_name]} Detector Position: {0[detector_name]}',
# 'type': 'sensor',
# 'entity': '{0[bay_id]}_position_{0[detector_id]}',
# 'value_template': '{{{{ value_json.{0[detector_id]} }}}}',
# 'unit_of_measurement': self._uom('length'),
# 'icon': 'mdi:ruler'
# }
# },
'bay_position': {
'topic': 'cobrabay/' + self._client_id + '/{0[bay_id]}/position',
'topic': 'cobrabay/' + self._client_id + '/{0[bay_id]}/{0[detector_id]}',
'previous_state': None,
'ha_discovery': {
'name': '{0[bay_name]} Detector Position: {0[detector_name]}',
'type': 'sensor',
'entity': '{0[bay_id]}_position_{0[detector_id]}',
'value_template': '{{{{ value_json.{0[detector_id]} }}}}',
'value_template': '{{{{ value_json.adjusted_reading }}}}',
'unit_of_measurement': self._uom('length'),
'icon': 'mdi:ruler'
'icon': 'mdi:ruler',
'json_attributes_topic': 'cobrabay/' + self._client_id + '/{0[bay_id]}/{0[detector_id]}'
}
},
# How good the parking job is.
Expand Down Expand Up @@ -320,13 +333,13 @@ def _cb_device_command(self, client, userdata, message):
message = json_loads(message.payload)
except:
self._logger_mqtt.error(
"Could not decode JSON from MQTT message '{}' on topic '{}'".format(topic, raw_message))
"Could not decode JSON from MQTT message '{}' on topic '{}'".format(message.topic, message))
# Ignore the error itself, plow through.
return False

# Proceed on valid commands.
if 'cmd' not in message:
self._logger_mqtt.error("MQTT message for topic {} does not contain a 'cmd' directive".format(topic))
self._logger_mqtt.error("MQTT message for topic {} does not contain a 'cmd' directive".format(message.topic))
elif message['cmd'] == 'rediscover':
# Rerun Home Assistant discovery
self._ha_discovery()
Expand Down

0 comments on commit 25ea88d

Please sign in to comment.