|
|
|
@ -28,13 +28,14 @@ class AmcrestMqtt(object):
|
|
|
|
|
|
|
|
|
|
|
|
self.storage_update_interval = config['amcrest'].get('storage_update_interval', 900)
|
|
|
|
self.storage_update_interval = config['amcrest'].get('storage_update_interval', 900)
|
|
|
|
self.snapshot_update_interval = config['amcrest'].get('snapshot_update_interval', 300)
|
|
|
|
self.snapshot_update_interval = config['amcrest'].get('snapshot_update_interval', 300)
|
|
|
|
|
|
|
|
self.discovery_complete = False
|
|
|
|
|
|
|
|
|
|
|
|
self.client_id = self.get_new_client_id()
|
|
|
|
self.client_id = self.get_new_client_id()
|
|
|
|
self.service_name = self.mqtt_config['prefix'] + ' service'
|
|
|
|
self.service_name = self.mqtt_config['prefix'] + ' service'
|
|
|
|
self.service_slug = self.mqtt_config['prefix'] + '-service'
|
|
|
|
self.service_slug = self.mqtt_config['prefix'] + '-service'
|
|
|
|
|
|
|
|
|
|
|
|
self.devices = {}
|
|
|
|
|
|
|
|
self.configs = {}
|
|
|
|
self.configs = {}
|
|
|
|
|
|
|
|
self.states = {}
|
|
|
|
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
def __enter__(self):
|
|
|
|
self.mqttc_create()
|
|
|
|
self.mqttc_create()
|
|
|
|
@ -124,7 +125,7 @@ class AmcrestMqtt(object):
|
|
|
|
# if we just started, we might get messages immediately, lets
|
|
|
|
# if we just started, we might get messages immediately, lets
|
|
|
|
# wait up to 3 min for devices to show up before we ignore the message
|
|
|
|
# wait up to 3 min for devices to show up before we ignore the message
|
|
|
|
checks = 0
|
|
|
|
checks = 0
|
|
|
|
while device_id not in self.configs:
|
|
|
|
while device_id not in self.states:
|
|
|
|
checks += 1
|
|
|
|
checks += 1
|
|
|
|
# we'll try for 3 min, and then give up
|
|
|
|
# we'll try for 3 min, and then give up
|
|
|
|
if checks > 36:
|
|
|
|
if checks > 36:
|
|
|
|
@ -132,7 +133,7 @@ class AmcrestMqtt(object):
|
|
|
|
return
|
|
|
|
return
|
|
|
|
time.sleep(5)
|
|
|
|
time.sleep(5)
|
|
|
|
|
|
|
|
|
|
|
|
self.logger.info(f'Got MQTT message for: {self.configs[device_id]["device"]["name"]} - {payload}')
|
|
|
|
self.logger.info(f'Got MQTT message for: {self.states[device_id]["device"]["name"]} - {payload}')
|
|
|
|
|
|
|
|
|
|
|
|
# ok, lets format the device_id (not needed) and send to amcrest
|
|
|
|
# ok, lets format the device_id (not needed) and send to amcrest
|
|
|
|
self.send_command(device_id, payload)
|
|
|
|
self.send_command(device_id, payload)
|
|
|
|
@ -234,14 +235,14 @@ class AmcrestMqtt(object):
|
|
|
|
# Service Device ------------------------------------------------------------------------------
|
|
|
|
# Service Device ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def publish_service_state(self):
|
|
|
|
def publish_service_state(self):
|
|
|
|
if 'service' not in self.configs:
|
|
|
|
if 'service' not in self.states:
|
|
|
|
self.configs['service'] = {
|
|
|
|
self.states['service'] = {
|
|
|
|
'availability': 'online',
|
|
|
|
'availability': 'online',
|
|
|
|
'state': { 'state': 'ON' },
|
|
|
|
'state': { 'state': 'ON' },
|
|
|
|
'intervals': {},
|
|
|
|
'intervals': {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
service_states = self.configs['service']
|
|
|
|
service_states = self.states['service']
|
|
|
|
|
|
|
|
|
|
|
|
# update states
|
|
|
|
# update states
|
|
|
|
service_states['state'] = {
|
|
|
|
service_states['state'] = {
|
|
|
|
@ -338,16 +339,16 @@ class AmcrestMqtt(object):
|
|
|
|
|
|
|
|
|
|
|
|
if 'device_type' in config:
|
|
|
|
if 'device_type' in config:
|
|
|
|
first = False
|
|
|
|
first = False
|
|
|
|
if device_id not in self.devices:
|
|
|
|
if device_id not in self.configs:
|
|
|
|
first = True
|
|
|
|
first = True
|
|
|
|
self.devices[device_id] = {}
|
|
|
|
self.configs[device_id] = {}
|
|
|
|
self.configs[device_id] = config
|
|
|
|
self.states[device_id] = config
|
|
|
|
self.devices[device_id]['qos'] = self.mqtt_config['qos']
|
|
|
|
self.configs[device_id]['qos'] = self.mqtt_config['qos']
|
|
|
|
self.devices[device_id]['state_topic'] = self.get_discovery_topic(device_id, 'state')
|
|
|
|
self.configs[device_id]['state_topic'] = self.get_discovery_topic(device_id, 'state')
|
|
|
|
self.devices[device_id]['availability_topic'] = self.get_discovery_topic('service', 'availability')
|
|
|
|
self.configs[device_id]['availability_topic'] = self.get_discovery_topic('service', 'availability')
|
|
|
|
self.devices[device_id]['command_topic'] = self.get_discovery_topic(device_id, 'set')
|
|
|
|
self.configs[device_id]['command_topic'] = self.get_discovery_topic(device_id, 'set')
|
|
|
|
|
|
|
|
|
|
|
|
self.devices[device_id]['device'] = {
|
|
|
|
self.configs[device_id]['device'] = {
|
|
|
|
'name': config['device_name'],
|
|
|
|
'name': config['device_name'],
|
|
|
|
'manufacturer': config['vendor'],
|
|
|
|
'manufacturer': config['vendor'],
|
|
|
|
'model': config['device_type'],
|
|
|
|
'model': config['device_type'],
|
|
|
|
@ -362,39 +363,46 @@ class AmcrestMqtt(object):
|
|
|
|
'configuration_url': 'http://' + config['host'] + '/',
|
|
|
|
'configuration_url': 'http://' + config['host'] + '/',
|
|
|
|
'via_device': self.service_slug,
|
|
|
|
'via_device': self.service_slug,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.devices[device_id]['origin'] = {
|
|
|
|
self.configs[device_id]['origin'] = {
|
|
|
|
'name': self.service_name,
|
|
|
|
'name': self.service_name,
|
|
|
|
'sw_version': self.version,
|
|
|
|
'sw_version': self.version,
|
|
|
|
'support_url': 'https://github.com/weirdtangent/amcrest2mqtt',
|
|
|
|
'support_url': 'https://github.com/weirdtangent/amcrest2mqtt',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# setup initial satte
|
|
|
|
|
|
|
|
self.states[device_id]['state'] = {
|
|
|
|
|
|
|
|
'state': 'ON',
|
|
|
|
|
|
|
|
'last_update': None,
|
|
|
|
|
|
|
|
'host': config['host'],
|
|
|
|
|
|
|
|
'serial_number': config['serial_number'],
|
|
|
|
|
|
|
|
'sw_version': config['software_version'],
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.add_components_to_device(device_id)
|
|
|
|
self.add_components_to_device(device_id)
|
|
|
|
|
|
|
|
|
|
|
|
if first:
|
|
|
|
if first:
|
|
|
|
self.logger.info(f'Adding device: "{config['device_name']}" [Amcrest {config["device_type"]}] ({device_id})')
|
|
|
|
self.logger.info(f'Adding device: "{config['device_name']}" [Amcrest {config["device_type"]}] ({device_id})')
|
|
|
|
self.publish_device_discovery(device_id)
|
|
|
|
self.publish_device_discovery(device_id)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
self.logger.debug(f'Updated device: {self.devices[device_id]['device']['name']}')
|
|
|
|
self.logger.debug(f'Updated device: {self.configs[device_id]['device']['name']}')
|
|
|
|
|
|
|
|
|
|
|
|
# device discovery sent, now it is save to add these to the
|
|
|
|
|
|
|
|
# dict (so they aren't included in device discovery object itself)
|
|
|
|
|
|
|
|
self.devices[device_id]['state'] = {
|
|
|
|
|
|
|
|
'status': 'online',
|
|
|
|
|
|
|
|
'host': config['host'],
|
|
|
|
|
|
|
|
'serial_number': config['serial_number'],
|
|
|
|
|
|
|
|
'sw_version': config['software_version'],
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
if first_time_through:
|
|
|
|
if first_time_through:
|
|
|
|
self.logger.info(f'Saw device, but not supported yet: "{config["device_name"]}" [amcrest {config["device_type"]}] ({device_id})')
|
|
|
|
self.logger.info(f'Saw device, but not supported yet: "{config["device_name"]}" [amcrest {config["device_type"]}] ({device_id})')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# lets log our first time through and then release the hounds
|
|
|
|
|
|
|
|
if not self.discovery_complete:
|
|
|
|
|
|
|
|
self.logger.info('Device setup and discovery is done')
|
|
|
|
|
|
|
|
self.discovery_complete = True
|
|
|
|
|
|
|
|
|
|
|
|
# add amcrest components to devices
|
|
|
|
# add amcrest components to devices
|
|
|
|
def add_components_to_device(self, device_id):
|
|
|
|
def add_components_to_device(self, device_id):
|
|
|
|
device = self.devices[device_id]
|
|
|
|
device_config = self.configs[device_id]
|
|
|
|
config = self.configs[device_id]
|
|
|
|
device_states = self.states[device_id]
|
|
|
|
components = {}
|
|
|
|
components = {}
|
|
|
|
|
|
|
|
|
|
|
|
if config['is_doorbell']:
|
|
|
|
if device_states['is_doorbell']:
|
|
|
|
doorbell_name = 'Doorbell' if config['device_name'] == 'Doorbell' else f'{config["device_name"]} Doorbell'
|
|
|
|
doorbell_name = 'Doorbell' if device_states['device_name'] == 'Doorbell' else f'{device_states["device_name"]} Doorbell'
|
|
|
|
components[self.get_slug(device_id, 'doorbell')] = {
|
|
|
|
components[self.get_slug(device_id, 'doorbell')] = {
|
|
|
|
'name': doorbell_name,
|
|
|
|
'name': doorbell_name,
|
|
|
|
'platform': 'binary_sensor',
|
|
|
|
'platform': 'binary_sensor',
|
|
|
|
@ -406,14 +414,9 @@ class AmcrestMqtt(object):
|
|
|
|
'value_template': '{{ value_json.doorbell }}',
|
|
|
|
'value_template': '{{ value_json.doorbell }}',
|
|
|
|
'unique_id': self.get_slug(device_id, 'doorbell'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'doorbell'),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.mqttc.will_set(
|
|
|
|
device_states['doorbell'] = {}
|
|
|
|
self.get_discovery_topic(device_id,'doorbell'),
|
|
|
|
|
|
|
|
payload=None,
|
|
|
|
|
|
|
|
qos=self.mqtt_config['qos'],
|
|
|
|
|
|
|
|
retain=True
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if config['is_ad410']:
|
|
|
|
if device_states['is_ad410']:
|
|
|
|
components[self.get_slug(device_id, 'human')] = {
|
|
|
|
components[self.get_slug(device_id, 'human')] = {
|
|
|
|
'name': 'Human',
|
|
|
|
'name': 'Human',
|
|
|
|
'platform': 'binary_sensor',
|
|
|
|
'platform': 'binary_sensor',
|
|
|
|
@ -424,19 +427,14 @@ class AmcrestMqtt(object):
|
|
|
|
'value_template': '{{ value_json.human }}',
|
|
|
|
'value_template': '{{ value_json.human }}',
|
|
|
|
'unique_id': self.get_slug(device_id, 'human'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'human'),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.mqttc.will_set(
|
|
|
|
device_states['human'] = {}
|
|
|
|
self.get_discovery_topic(device_id,'human'),
|
|
|
|
|
|
|
|
payload=None,
|
|
|
|
|
|
|
|
qos=self.mqtt_config['qos'],
|
|
|
|
|
|
|
|
retain=True
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components[self.get_slug(device_id, 'camera')] = {
|
|
|
|
components[self.get_slug(device_id, 'camera')] = {
|
|
|
|
'name': 'Camera',
|
|
|
|
'name': 'Camera',
|
|
|
|
'platform': 'camera',
|
|
|
|
'platform': 'camera',
|
|
|
|
'topic': self.get_discovery_subtopic(device_id, 'camera','snapshot'),
|
|
|
|
'topic': self.get_discovery_subtopic(device_id, 'camera','snapshot'),
|
|
|
|
'image_encoding': 'b64',
|
|
|
|
'image_encoding': 'b64',
|
|
|
|
'state_topic': device['state_topic'],
|
|
|
|
'state_topic': device_config['state_topic'],
|
|
|
|
'value_template': '{{ value_json.state }}',
|
|
|
|
'value_template': '{{ value_json.state }}',
|
|
|
|
'unique_id': self.get_slug(device_id, 'camera'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'camera'),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -447,7 +445,8 @@ class AmcrestMqtt(object):
|
|
|
|
rtc_link = webrtc_config['link']
|
|
|
|
rtc_link = webrtc_config['link']
|
|
|
|
rtc_source = webrtc_config['sources'].pop(0)
|
|
|
|
rtc_source = webrtc_config['sources'].pop(0)
|
|
|
|
rtc_url = f'http://{rtc_host}:{rtc_port}/{rtc_link}?src={rtc_source}'
|
|
|
|
rtc_url = f'http://{rtc_host}:{rtc_port}/{rtc_link}?src={rtc_source}'
|
|
|
|
device['device']['configuration_url'] = rtc_url
|
|
|
|
device_config['device']['configuration_url'] = rtc_url
|
|
|
|
|
|
|
|
device_states['camera'] = {'snapshot': None}
|
|
|
|
|
|
|
|
|
|
|
|
components[self.get_slug(device_id, 'motion')] = {
|
|
|
|
components[self.get_slug(device_id, 'motion')] = {
|
|
|
|
'name': 'Motion',
|
|
|
|
'name': 'Motion',
|
|
|
|
@ -458,12 +457,13 @@ class AmcrestMqtt(object):
|
|
|
|
'state_topic': self.get_discovery_topic(device_id, 'motion'),
|
|
|
|
'state_topic': self.get_discovery_topic(device_id, 'motion'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'motion'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'motion'),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
device_states['motion'] = {}
|
|
|
|
|
|
|
|
|
|
|
|
components[self.get_slug(device_id, 'version')] = {
|
|
|
|
components[self.get_slug(device_id, 'version')] = {
|
|
|
|
'name': 'Version',
|
|
|
|
'name': 'Version',
|
|
|
|
'platform': 'sensor',
|
|
|
|
'platform': 'sensor',
|
|
|
|
'icon': 'mdi:package-up',
|
|
|
|
'icon': 'mdi:package-up',
|
|
|
|
'state_topic': device['state_topic'],
|
|
|
|
'state_topic': device_config['state_topic'],
|
|
|
|
'value_template': '{{ value_json.sw_version }}',
|
|
|
|
'value_template': '{{ value_json.sw_version }}',
|
|
|
|
'entity_category': 'diagnostic',
|
|
|
|
'entity_category': 'diagnostic',
|
|
|
|
'unique_id': self.get_slug(device_id, 'sw_version'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'sw_version'),
|
|
|
|
@ -473,7 +473,7 @@ class AmcrestMqtt(object):
|
|
|
|
'name': 'Serial Number',
|
|
|
|
'name': 'Serial Number',
|
|
|
|
'platform': 'sensor',
|
|
|
|
'platform': 'sensor',
|
|
|
|
'icon': 'mdi:identifier',
|
|
|
|
'icon': 'mdi:identifier',
|
|
|
|
'state_topic': device['state_topic'],
|
|
|
|
'state_topic': device_config['state_topic'],
|
|
|
|
'value_template': '{{ value_json.serial_number }}',
|
|
|
|
'value_template': '{{ value_json.serial_number }}',
|
|
|
|
'entity_category': 'diagnostic',
|
|
|
|
'entity_category': 'diagnostic',
|
|
|
|
'unique_id': self.get_slug(device_id, 'serial_number'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'serial_number'),
|
|
|
|
@ -483,7 +483,7 @@ class AmcrestMqtt(object):
|
|
|
|
'name': 'Host',
|
|
|
|
'name': 'Host',
|
|
|
|
'platform': 'sensor',
|
|
|
|
'platform': 'sensor',
|
|
|
|
'icon': 'mdi:ip-network',
|
|
|
|
'icon': 'mdi:ip-network',
|
|
|
|
'state_topic': device['state_topic'],
|
|
|
|
'state_topic': device_config['state_topic'],
|
|
|
|
'value_template': '{{ value_json.host }}',
|
|
|
|
'value_template': '{{ value_json.host }}',
|
|
|
|
'entity_category': 'diagnostic',
|
|
|
|
'entity_category': 'diagnostic',
|
|
|
|
'unique_id': self.get_slug(device_id, 'host'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'host'),
|
|
|
|
@ -495,6 +495,8 @@ class AmcrestMqtt(object):
|
|
|
|
'state_topic': self.get_discovery_topic(device_id, 'event'),
|
|
|
|
'state_topic': self.get_discovery_topic(device_id, 'event'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'event'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'event'),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
device_states['event'] = {}
|
|
|
|
|
|
|
|
device_states['recording'] = {}
|
|
|
|
|
|
|
|
|
|
|
|
components[self.get_slug(device_id, 'storage_used_percent')] = {
|
|
|
|
components[self.get_slug(device_id, 'storage_used_percent')] = {
|
|
|
|
'name': 'Storage Used %',
|
|
|
|
'name': 'Storage Used %',
|
|
|
|
@ -523,61 +525,51 @@ class AmcrestMqtt(object):
|
|
|
|
'value_template': '{{ value_json.used }}',
|
|
|
|
'value_template': '{{ value_json.used }}',
|
|
|
|
'unique_id': self.get_slug(device_id, 'storage_used'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'storage_used'),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
device_states['storage'] = {}
|
|
|
|
|
|
|
|
|
|
|
|
components[self.get_slug(device_id, 'last_update')] = {
|
|
|
|
components[self.get_slug(device_id, 'last_update')] = {
|
|
|
|
'name': 'Last Update',
|
|
|
|
'name': 'Last Update',
|
|
|
|
'platform': 'sensor',
|
|
|
|
'platform': 'sensor',
|
|
|
|
'device_class': 'timestamp',
|
|
|
|
'device_class': 'timestamp',
|
|
|
|
'entity_category': 'diagnostic',
|
|
|
|
'entity_category': 'diagnostic',
|
|
|
|
'state_topic': device['state_topic'],
|
|
|
|
'state_topic': device_config['state_topic'],
|
|
|
|
'value_template': '{{ value_json.last_update }}',
|
|
|
|
'value_template': '{{ value_json.last_update }}',
|
|
|
|
'unique_id': self.get_slug(device_id, 'last_update'),
|
|
|
|
'unique_id': self.get_slug(device_id, 'last_update'),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
device['components'] = components
|
|
|
|
device_config['components'] = components
|
|
|
|
|
|
|
|
|
|
|
|
def publish_device_state(self, device_id):
|
|
|
|
def publish_device_state(self, device_id):
|
|
|
|
device = self.devices[device_id]
|
|
|
|
device_states = self.states[device_id]
|
|
|
|
config = self.configs[device_id]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for topic in ['state','storage','motion','human','doorbell','event','recording']:
|
|
|
|
for topic in ['state','storage','motion','human','doorbell','event','recording']:
|
|
|
|
if topic in device:
|
|
|
|
if topic in device_states:
|
|
|
|
payload = json.dumps(device[topic]) if isinstance(device[topic], dict) else device[topic]
|
|
|
|
payload = json.dumps(device_states[topic]) if isinstance(device_states[topic], dict) else device_states[topic]
|
|
|
|
self.mqttc.publish(self.get_discovery_topic(device_id, topic), payload, qos=self.mqtt_config['qos'], retain=True)
|
|
|
|
self.mqttc.publish(self.get_discovery_topic(device_id, topic), payload, qos=self.mqtt_config['qos'], retain=True)
|
|
|
|
|
|
|
|
|
|
|
|
if 'snapshot' in device['camera'] and device['camera']['snapshot'] is not None:
|
|
|
|
if 'snapshot' in device_states['camera'] and device_states['camera']['snapshot'] is not None:
|
|
|
|
self.logger.info(f'Refreshing snapshot for {config["device_name"]}')
|
|
|
|
payload = device_states['camera']['snapshot']
|
|
|
|
payload = device['camera']['snapshot']
|
|
|
|
|
|
|
|
result = self.mqttc.publish(self.get_discovery_subtopic(device_id, 'camera','snapshot'), payload, qos=self.mqtt_config['qos'], retain=True)
|
|
|
|
result = self.mqttc.publish(self.get_discovery_subtopic(device_id, 'camera','snapshot'), payload, qos=self.mqtt_config['qos'], retain=True)
|
|
|
|
|
|
|
|
|
|
|
|
def publish_device_discovery(self, device_id):
|
|
|
|
def publish_device_discovery(self, device_id):
|
|
|
|
device = self.devices[device_id]
|
|
|
|
device_config = self.configs[device_id]
|
|
|
|
self.mqttc.publish(
|
|
|
|
payload = json.dumps(device_config)
|
|
|
|
self.get_discovery_topic(device_id, 'config'),
|
|
|
|
|
|
|
|
json.dumps(device),
|
|
|
|
|
|
|
|
qos=self.mqtt_config['qos'],
|
|
|
|
|
|
|
|
retain=True
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# setup initial states
|
|
|
|
self.mqttc.publish(self.get_discovery_topic(device_id, 'config'), payload, qos=self.mqtt_config['qos'], retain=True)
|
|
|
|
device['state'] = { 'state': 'ON' }
|
|
|
|
|
|
|
|
device['motion'] = 'off'
|
|
|
|
|
|
|
|
device['camera'] = { 'snapshot': None }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# refresh * all devices -----------------------------------------------------------------------
|
|
|
|
# refresh * all devices -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def refresh_storage_all_devices(self):
|
|
|
|
def refresh_storage_all_devices(self):
|
|
|
|
self.logger.info(f'Refreshing storage info for all devices (every {self.storage_update_interval} sec)')
|
|
|
|
self.logger.info(f'Refreshing storage info for all devices (every {self.storage_update_interval} sec)')
|
|
|
|
|
|
|
|
|
|
|
|
for device_id in self.devices:
|
|
|
|
for device_id in self.configs:
|
|
|
|
if not self.running: break
|
|
|
|
if not self.running: break
|
|
|
|
|
|
|
|
device_states = self.states[device_id]
|
|
|
|
device = self.devices[device_id]
|
|
|
|
|
|
|
|
config = self.configs[device_id]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# get the storage info, pull out last_update and save that to the device state
|
|
|
|
# get the storage info, pull out last_update and save that to the device state
|
|
|
|
storage = self.amcrestc.get_device_storage_stats(device_id)
|
|
|
|
storage = self.amcrestc.get_device_storage_stats(device_id)
|
|
|
|
device['state']['last_update'] = storage.pop('last_update', None)
|
|
|
|
device_states['state']['last_update'] = storage.pop('last_update', None)
|
|
|
|
device['storage'] = storage
|
|
|
|
device_states['storage'] = storage
|
|
|
|
|
|
|
|
|
|
|
|
self.publish_service_state()
|
|
|
|
self.publish_service_state()
|
|
|
|
self.publish_device_state(device_id)
|
|
|
|
self.publish_device_state(device_id)
|
|
|
|
@ -585,24 +577,27 @@ class AmcrestMqtt(object):
|
|
|
|
def refresh_snapshot_all_devices(self):
|
|
|
|
def refresh_snapshot_all_devices(self):
|
|
|
|
self.logger.info(f'Collecting snapshots for all devices (every {self.snapshot_update_interval} sec)')
|
|
|
|
self.logger.info(f'Collecting snapshots for all devices (every {self.snapshot_update_interval} sec)')
|
|
|
|
|
|
|
|
|
|
|
|
for device_id in self.devices:
|
|
|
|
for device_id in self.configs:
|
|
|
|
if not self.running: break
|
|
|
|
if not self.running: break
|
|
|
|
|
|
|
|
self.refresh_snapshot(device_id)
|
|
|
|
|
|
|
|
|
|
|
|
device = self.devices[device_id]
|
|
|
|
def refresh_snapshot(self, device_id):
|
|
|
|
config = self.configs[device_id]
|
|
|
|
device_states = self.states[device_id]
|
|
|
|
|
|
|
|
image = self.amcrestc.get_snapshot(device_id)
|
|
|
|
device['camera']['snapshot'] = self.amcrestc.get_snapshot(device_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# only store and send to MQTT if the image has changed
|
|
|
|
|
|
|
|
if device_states['camera']['snapshot'] is None or device_states['camera']['snapshot'] != image:
|
|
|
|
|
|
|
|
device_states['camera']['snapshot'] = image
|
|
|
|
self.publish_service_state()
|
|
|
|
self.publish_service_state()
|
|
|
|
self.publish_device_state(device_id)
|
|
|
|
self.publish_device_state(device_id)
|
|
|
|
|
|
|
|
|
|
|
|
# send command to Amcrest --------------------------------------------------------------------
|
|
|
|
# send command to Amcrest --------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def send_command(self, device_id, data):
|
|
|
|
def send_command(self, device_id, data):
|
|
|
|
device = self.devices[device_id]
|
|
|
|
device_config = self.configs[device_id]
|
|
|
|
config = self.configs[device_id]
|
|
|
|
device_states = self.states[device_id]
|
|
|
|
|
|
|
|
|
|
|
|
self.logger.info(f'COMMAND {config["device_name"]} = {data}')
|
|
|
|
self.logger.info(f'COMMAND {device_states["device_name"]} = {data}')
|
|
|
|
|
|
|
|
|
|
|
|
if data == 'PRESS':
|
|
|
|
if data == 'PRESS':
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
@ -623,28 +618,38 @@ class AmcrestMqtt(object):
|
|
|
|
|
|
|
|
|
|
|
|
self.publish_service_state()
|
|
|
|
self.publish_service_state()
|
|
|
|
|
|
|
|
|
|
|
|
# check for events ----------------------------------------------------------------------------
|
|
|
|
# collect events and then check queue of events -----------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def collect_all_device_events(self):
|
|
|
|
|
|
|
|
await self.amcrestc.collect_all_device_events()
|
|
|
|
|
|
|
|
|
|
|
|
def check_for_events(self):
|
|
|
|
def check_for_events(self):
|
|
|
|
while device_event := self.amcrestc.get_next_event():
|
|
|
|
while device_event := self.amcrestc.get_next_event():
|
|
|
|
|
|
|
|
if device_event is None:
|
|
|
|
|
|
|
|
break
|
|
|
|
if 'device_id' not in device_event:
|
|
|
|
if 'device_id' not in device_event:
|
|
|
|
self.logger(f'Got event, but missing device_id: {device_event}')
|
|
|
|
self.logger(f'Got event, but missing device_id: {device_event}')
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
device_id = device_event['device_id']
|
|
|
|
device_id = device_event['device_id']
|
|
|
|
event = device_event['event']
|
|
|
|
event = device_event['event']
|
|
|
|
payload = device_event['payload']
|
|
|
|
payload = device_event['payload']
|
|
|
|
device = self.devices[device_id]
|
|
|
|
|
|
|
|
config = self.configs[device_id]
|
|
|
|
device_states = self.states[device_id]
|
|
|
|
|
|
|
|
|
|
|
|
# if one of our known sensors
|
|
|
|
# if one of our known sensors
|
|
|
|
if event in ['motion','human','doorbell','recording']:
|
|
|
|
if event in ['motion','human','doorbell','recording']:
|
|
|
|
self.logger.info(f'Got event for {config["device_name"]}: {event}')
|
|
|
|
self.logger.info(f'Got event for {device_states["device_name"]}: {event}')
|
|
|
|
device[event] = payload
|
|
|
|
device_states[event] = payload
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# any of these could mean a new snapshot is available early, lets try to grab it
|
|
|
|
|
|
|
|
self.logger.debug(f'Refreshing snapshot for "{device_states["device_name"]}" early because of event')
|
|
|
|
|
|
|
|
self.refresh_snapshot(device_id)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
self.logger.debug(f'Got "other" event for {config["device_name"]}: {event} {payload}')
|
|
|
|
self.logger.info(f'Got "other" event for "{device_states["device_name"]}": {event} {payload}')
|
|
|
|
device['event'] = event
|
|
|
|
device_states['event'] = event
|
|
|
|
|
|
|
|
|
|
|
|
self.refresh_device(device_id)
|
|
|
|
self.publish_device_state(device_id)
|
|
|
|
|
|
|
|
|
|
|
|
# async loops and main loop -------------------------------------------------------------------
|
|
|
|
# async loops and main loop -------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
@ -662,7 +667,11 @@ class AmcrestMqtt(object):
|
|
|
|
|
|
|
|
|
|
|
|
async def collect_events(self):
|
|
|
|
async def collect_events(self):
|
|
|
|
while self.running == True:
|
|
|
|
while self.running == True:
|
|
|
|
await self.amcrestc.collect_all_device_events()
|
|
|
|
await self.collect_all_device_events()
|
|
|
|
|
|
|
|
await asyncio.sleep(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def check_event_queue(self):
|
|
|
|
|
|
|
|
while self.running == True:
|
|
|
|
self.check_for_events()
|
|
|
|
self.check_for_events()
|
|
|
|
await asyncio.sleep(1)
|
|
|
|
await asyncio.sleep(1)
|
|
|
|
|
|
|
|
|
|
|
|
@ -680,6 +689,7 @@ class AmcrestMqtt(object):
|
|
|
|
tasks = [
|
|
|
|
tasks = [
|
|
|
|
asyncio.create_task(self.collect_storage_info()),
|
|
|
|
asyncio.create_task(self.collect_storage_info()),
|
|
|
|
asyncio.create_task(self.collect_events()),
|
|
|
|
asyncio.create_task(self.collect_events()),
|
|
|
|
|
|
|
|
asyncio.create_task(self.check_event_queue()),
|
|
|
|
asyncio.create_task(self.collect_snapshots()),
|
|
|
|
asyncio.create_task(self.collect_snapshots()),
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|