Cam Soper 3 years ago
commit 0156c3cccf

@ -12,17 +12,16 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2.3.4 uses: actions/checkout@v2.4.0
- name: Bump version - name: Bump version
uses: remorses/bump-version@js uses: remorses/bump-version@js
id: version id: version
with: with:
version_file: ./VERSION version_file: ./VERSION
github_token: ${{ secrets.GITHUB_TOKEN }}
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2.3.4 uses: actions/checkout@v2.4.0
with: with:
ref: main ref: main
- name: Set up QEMU - name: Set up QEMU
@ -30,14 +29,14 @@ jobs:
with: with:
platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1.5.1 uses: docker/setup-buildx-action@v1.6.0
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v1.10.0 uses: docker/login-action@v1.12.0
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v2.6.1 uses: docker/build-push-action@v2.7.0
with: with:
context: . context: .
push: true push: true

@ -8,6 +8,7 @@ COPY requirements.txt /
RUN pip install --no-warn-script-location --prefix=/install -r /requirements.txt RUN pip install --no-warn-script-location --prefix=/install -r /requirements.txt
FROM base FROM base
STOPSIGNAL SIGINT
COPY --from=builder /install /usr/local COPY --from=builder /install /usr/local
COPY src /app COPY src /app
COPY VERSION /app COPY VERSION /app

@ -22,6 +22,7 @@ It supports the following environment variables:
- `HOME_ASSISTANT` (optional, default = false) - `HOME_ASSISTANT` (optional, default = false)
- `HOME_ASSISTANT_PREFIX` (optional, default = 'homeassistant') - `HOME_ASSISTANT_PREFIX` (optional, default = 'homeassistant')
- `STORAGE_POLL_INTERVAL` (optional, default = 3600) - how often to fetch storage data (in seconds) - `STORAGE_POLL_INTERVAL` (optional, default = 3600) - how often to fetch storage data (in seconds)
- `DEVICE_NAME` (optional) - override the default device name used in the Amcrest app
It exposes events to the following topics: It exposes events to the following topics:
@ -29,6 +30,7 @@ It exposes events to the following topics:
- `amcrest2mqtt/[SERIAL_NUMBER]/doorbell` - doorbell status (if AD110 or AD410) - `amcrest2mqtt/[SERIAL_NUMBER]/doorbell` - doorbell status (if AD110 or AD410)
- `amcrest2mqtt/[SERIAL_NUMBER]/human` - human detection (if AD410) - `amcrest2mqtt/[SERIAL_NUMBER]/human` - human detection (if AD410)
- `amcrest2mqtt/[SERIAL_NUMBER]/motion` - motion events (if supported) - `amcrest2mqtt/[SERIAL_NUMBER]/motion` - motion events (if supported)
- `amcrest2mqtt/[SERIAL_NUMBER]/config` - device configuration information
## Device Support ## Device Support
@ -63,7 +65,7 @@ services:
### Multiple Devices ### Multiple Devices
The app will not support multiple devices. You can run multiple instances of the app if you need to expose events for multiple devies. The app will not support multiple devices. You can run multiple instances of the app if you need to expose events for multiple devices.
### Non-Docker Environments ### Non-Docker Environments

@ -1 +1 @@
1.0.9 1.0.15

@ -1,3 +1,3 @@
amcrest==1.8.0 amcrest==1.9.3
paho-mqtt==1.5.1 paho-mqtt==1.6.1
python-slugify==5.0.2 python-slugify==5.0.2

@ -17,8 +17,10 @@ amcrest_host = os.getenv("AMCREST_HOST")
amcrest_port = int(os.getenv("AMCREST_PORT") or 80) amcrest_port = int(os.getenv("AMCREST_PORT") or 80)
amcrest_username = os.getenv("AMCREST_USERNAME") or "admin" amcrest_username = os.getenv("AMCREST_USERNAME") or "admin"
amcrest_password = os.getenv("AMCREST_PASSWORD") amcrest_password = os.getenv("AMCREST_PASSWORD")
storage_poll_interval = int(os.getenv("STORAGE_POLL_INTERVAL") or 3600) storage_poll_interval = int(os.getenv("STORAGE_POLL_INTERVAL") or 3600)
camera_ping_enabled = os.getenv("CAMERA_PING_ENABLED") == "true" camera_ping_enabled = os.getenv("CAMERA_PING_ENABLED") == "true"
device_name = os.getenv("DEVICE_NAME")
mqtt_host = os.getenv("MQTT_HOST") or "localhost" mqtt_host = os.getenv("MQTT_HOST") or "localhost"
mqtt_qos = int(os.getenv("MQTT_QOS") or 0) mqtt_qos = int(os.getenv("MQTT_QOS") or 0)
@ -93,12 +95,16 @@ def refresh_storage_sensors():
try: try:
storage = camera.storage_all storage = camera.storage_all
mqtt_publish(topics["storage_used_percent"], str(storage["used_percent"])) mqtt_publish(topics["storage_used_percent"], str(storage["used_percent"]))
mqtt_publish(topics["storage_used"], str(storage["used"][0])) mqtt_publish(topics["storage_used"], to_gb(storage["used"]))
mqtt_publish(topics["storage_total"], str(storage["total"][0])) mqtt_publish(topics["storage_total"], to_gb(storage["total"]))
except AmcrestError as error: except AmcrestError as error:
log(f"Error fetching storage information {error}", level="WARNING") log(f"Error fetching storage information {error}", level="WARNING")
def to_gb(total):
return str(round(float(total[0]) / 1024 / 1024 / 1024, 2))
def ping_camera(): def ping_camera():
Timer(30, ping_camera).start() Timer(30, ping_camera).start()
response = os.system(f"ping -c1 -W100 {amcrest_host} >/dev/null 2>&1") response = os.system(f"ping -c1 -W100 {amcrest_host} >/dev/null 2>&1")
@ -150,9 +156,16 @@ try:
is_ad110 = device_type == "AD110" is_ad110 = device_type == "AD110"
is_ad410 = device_type == "AD410" is_ad410 = device_type == "AD410"
is_doorbell = is_ad110 or is_ad410 is_doorbell = is_ad110 or is_ad410
serial_number = camera.serial_number.strip() serial_number = camera.serial_number
if not isinstance(serial_number, str):
log(f"Error fetching serial number", level="ERROR")
exit_gracefully(1)
sw_version = camera.software_information[0].replace("version=", "").strip() sw_version = camera.software_information[0].replace("version=", "").strip()
device_name = camera.machine_name.replace("name=", "").strip() if not device_name:
device_name = camera.machine_name.replace("name=", "").strip()
device_slug = slugify(device_name, separator="_") device_slug = slugify(device_name, separator="_")
except AmcrestError as error: except AmcrestError as error:
log(f"Error fetching camera details", level="ERROR") log(f"Error fetching camera details", level="ERROR")
@ -174,13 +187,27 @@ topics = {
"storage_used": f"amcrest2mqtt/{serial_number}/storage/used", "storage_used": f"amcrest2mqtt/{serial_number}/storage/used",
"storage_used_percent": f"amcrest2mqtt/{serial_number}/storage/used_percent", "storage_used_percent": f"amcrest2mqtt/{serial_number}/storage/used_percent",
"storage_total": f"amcrest2mqtt/{serial_number}/storage/total", "storage_total": f"amcrest2mqtt/{serial_number}/storage/total",
"home_assistant": { "home_assistant_legacy": {
"doorbell": f"{home_assistant_prefix}/binary_sensor/amcrest2mqtt-{serial_number}/{device_slug}_doorbell/config", "doorbell": f"{home_assistant_prefix}/binary_sensor/amcrest2mqtt-{serial_number}/{device_slug}_doorbell/config",
"human": f"{home_assistant_prefix}/binary_sensor/amcrest2mqtt-{serial_number}/{device_slug}_human/config", "human": f"{home_assistant_prefix}/binary_sensor/amcrest2mqtt-{serial_number}/{device_slug}_human/config",
"motion": f"{home_assistant_prefix}/binary_sensor/amcrest2mqtt-{serial_number}/{device_slug}_motion/config", "motion": f"{home_assistant_prefix}/binary_sensor/amcrest2mqtt-{serial_number}/{device_slug}_motion/config",
"storage_used": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/{device_slug}_storage_used/config", "storage_used": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/{device_slug}_storage_used/config",
"storage_used_percent": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/{device_slug}_storage_used_percent/config", "storage_used_percent": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/{device_slug}_storage_used_percent/config",
"storage_total": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/{device_slug}_storage_total/config", "storage_total": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/{device_slug}_storage_total/config",
"version": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/{device_slug}_version/config",
"host": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/{device_slug}_host/config",
"serial_number": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/{device_slug}_serial_number/config",
},
"home_assistant": {
"doorbell": f"{home_assistant_prefix}/binary_sensor/amcrest2mqtt-{serial_number}/doorbell/config",
"human": f"{home_assistant_prefix}/binary_sensor/amcrest2mqtt-{serial_number}/human/config",
"motion": f"{home_assistant_prefix}/binary_sensor/amcrest2mqtt-{serial_number}/motion/config",
"storage_used": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/storage_used/config",
"storage_used_percent": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/storage_used_percent/config",
"storage_total": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/storage_total/config",
"version": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/version/config",
"host": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/host/config",
"serial_number": f"{home_assistant_prefix}/sensor/amcrest2mqtt-{serial_number}/serial_number/config",
}, },
} }
@ -236,6 +263,9 @@ if home_assistant:
} }
if is_doorbell: if is_doorbell:
doorbell_name = "Doorbell" if device_name == "Doorbell" else f"{device_name} Doorbell"
mqtt_publish(topics["home_assistant_legacy"]["doorbell"], "")
mqtt_publish( mqtt_publish(
topics["home_assistant"]["doorbell"], topics["home_assistant"]["doorbell"],
base_config base_config
@ -243,13 +273,15 @@ if home_assistant:
"state_topic": topics["doorbell"], "state_topic": topics["doorbell"],
"payload_on": "on", "payload_on": "on",
"payload_off": "off", "payload_off": "off",
"name": f"{device_name} Doorbell", "icon": "mdi:doorbell",
"name": doorbell_name,
"unique_id": f"{serial_number}.doorbell", "unique_id": f"{serial_number}.doorbell",
}, },
json=True, json=True,
) )
if is_ad410: if is_ad410:
mqtt_publish(topics["home_assistant_legacy"]["human"], "")
mqtt_publish( mqtt_publish(
topics["home_assistant"]["human"], topics["home_assistant"]["human"],
base_config base_config
@ -264,6 +296,7 @@ if home_assistant:
json=True, json=True,
) )
mqtt_publish(topics["home_assistant_legacy"]["motion"], "")
mqtt_publish( mqtt_publish(
topics["home_assistant"]["motion"], topics["home_assistant"]["motion"],
base_config base_config
@ -278,7 +311,56 @@ if home_assistant:
json=True, json=True,
) )
mqtt_publish(topics["home_assistant_legacy"]["version"], "")
mqtt_publish(
topics["home_assistant"]["version"],
base_config
| {
"state_topic": topics["config"],
"value_template": "{{ value_json.sw_version }}",
"icon": "mdi:package-up",
"name": f"{device_name} Version",
"unique_id": f"{serial_number}.version",
"entity_category": "diagnostic",
"enabled_by_default": False
},
json=True,
)
mqtt_publish(topics["home_assistant_legacy"]["serial_number"], "")
mqtt_publish(
topics["home_assistant"]["serial_number"],
base_config
| {
"state_topic": topics["config"],
"value_template": "{{ value_json.serial_number }}",
"icon": "mdi:alphabetical-variant",
"name": f"{device_name} Serial Number",
"unique_id": f"{serial_number}.serial_number",
"entity_category": "diagnostic",
"enabled_by_default": False
},
json=True,
)
mqtt_publish(topics["home_assistant_legacy"]["host"], "")
mqtt_publish(
topics["home_assistant"]["host"],
base_config
| {
"state_topic": topics["config"],
"value_template": "{{ value_json.host }}",
"icon": "mdi:ip-network",
"name": f"{device_name} Host",
"unique_id": f"{serial_number}.host",
"entity_category": "diagnostic",
"enabled_by_default": False
},
json=True,
)
if storage_poll_interval > 0: if storage_poll_interval > 0:
mqtt_publish(topics["home_assistant_legacy"]["storage_used_percent"], "")
mqtt_publish( mqtt_publish(
topics["home_assistant"]["storage_used_percent"], topics["home_assistant"]["storage_used_percent"],
base_config base_config
@ -287,11 +369,14 @@ if home_assistant:
"unit_of_measurement": "%", "unit_of_measurement": "%",
"icon": "mdi:micro-sd", "icon": "mdi:micro-sd",
"name": f"{device_name} Storage Used %", "name": f"{device_name} Storage Used %",
"object_id": f"{device_slug}_storage_used_percent",
"unique_id": f"{serial_number}.storage_used_percent", "unique_id": f"{serial_number}.storage_used_percent",
"entity_category": "diagnostic",
}, },
json=True, json=True,
) )
mqtt_publish(topics["home_assistant_legacy"]["storage_used"], "")
mqtt_publish( mqtt_publish(
topics["home_assistant"]["storage_used"], topics["home_assistant"]["storage_used"],
base_config base_config
@ -301,10 +386,12 @@ if home_assistant:
"icon": "mdi:micro-sd", "icon": "mdi:micro-sd",
"name": f"{device_name} Storage Used", "name": f"{device_name} Storage Used",
"unique_id": f"{serial_number}.storage_used", "unique_id": f"{serial_number}.storage_used",
"entity_category": "diagnostic",
}, },
json=True, json=True,
) )
mqtt_publish(topics["home_assistant_legacy"]["storage_total"], "")
mqtt_publish( mqtt_publish(
topics["home_assistant"]["storage_total"], topics["home_assistant"]["storage_total"],
base_config base_config
@ -314,6 +401,7 @@ if home_assistant:
"icon": "mdi:micro-sd", "icon": "mdi:micro-sd",
"name": f"{device_name} Storage Total", "name": f"{device_name} Storage Total",
"unique_id": f"{serial_number}.storage_total", "unique_id": f"{serial_number}.storage_total",
"entity_category": "diagnostic",
}, },
json=True, json=True,
) )
@ -326,6 +414,7 @@ mqtt_publish(topics["config"], {
"device_name": device_name, "device_name": device_name,
"sw_version": sw_version, "sw_version": sw_version,
"serial_number": serial_number, "serial_number": serial_number,
"host": amcrest_host,
}, json=True) }, json=True)
if storage_poll_interval > 0: if storage_poll_interval > 0:

Loading…
Cancel
Save