diff --git a/.semver b/.semver new file mode 100644 index 0000000..7dea76e --- /dev/null +++ b/.semver @@ -0,0 +1 @@ +1.0.1 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a30ad4a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,117 @@ +sudo: true +language: python +services: + - docker + +env: + global: + - PROJECT=aqara-mqtt + - IMAGE_NAME=$DOCKER_USERNAME/$PROJECT + - VERSION=$(cat .semver) + - MAJOR=$(cut -d. -f1 .semver) + - MINOR=$(cut -d. -f2 .semver) + - PATCH=$(cut -d. -f3 .semver) + +jobs: + include: + - stage: build x64 image + before_script: + - ARCH=x64 + + script: + # build + - docker build -t $PROJECT -f Dockerfile-$ARCH . + + # push image + - > + if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then + docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" + + #1.0.0-arch + docker tag $PROJECT $IMAGE_NAME:$VERSION-$ARCH + docker push $IMAGE_NAME:$VERSION-$ARCH + + #1.0-arch + docker tag $PROJECT $IMAGE_NAME:$MAJOR.$MINOR-$ARCH + docker push $IMAGE_NAME:$MAJOR.$MINOR-$ARCH + + #1-arch + docker tag $PROJECT $IMAGE_NAME:$MAJOR-$ARCH + docker push $IMAGE_NAME:$MAJOR-$ARCH + + #latest by arch + docker tag $PROJECT $IMAGE_NAME:$ARCH + docker push $IMAGE_NAME:$ARCH + fi + + - stage: build i386 image + before_script: + - ARCH=i386 + + script: + # build + - docker build -t $PROJECT -f Dockerfile-$ARCH . + + # push image + - > + if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then + docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" + + #1.0.0-arch + docker tag $PROJECT $IMAGE_NAME:$VERSION-$ARCH + docker push $IMAGE_NAME:$VERSION-$ARCH + + #1.0-arch + docker tag $PROJECT $IMAGE_NAME:$MAJOR.$MINOR-$ARCH + docker push $IMAGE_NAME:$MAJOR.$MINOR-$ARCH + + #1-arch + docker tag $PROJECT $IMAGE_NAME:$MAJOR-$ARCH + docker push $IMAGE_NAME:$MAJOR-$ARCH + + #latest by arch + docker tag $PROJECT $IMAGE_NAME:$ARCH + docker push $IMAGE_NAME:$ARCH + fi + + - stage: build armhf image + before_script: + - ARCH=armhf + + script: + # prepare qemu + - docker run --rm --privileged multiarch/qemu-user-static:register --reset + # get qemu-arm-static binary + - > + mkdir tmp && + pushd tmp && + curl -L -o qemu-arm-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/v2.6.0/qemu-arm-static.tar.gz && + tar xzf qemu-arm-static.tar.gz && + popd + #copy it to container + - sed -i -e '2iCOPY tmp/qemu-arm-static /usr/bin/qemu-arm-static\' Dockerfile-$ARCH + + # build + - docker build -t $PROJECT -f Dockerfile-$ARCH . + + # push image + - > + if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then + docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" + + #1.0.0-arch + docker tag $PROJECT $IMAGE_NAME:$VERSION-$ARCH + docker push $IMAGE_NAME:$VERSION-$ARCH + + #1.0-arch + docker tag $PROJECT $IMAGE_NAME:$MAJOR.$MINOR-$ARCH + docker push $IMAGE_NAME:$MAJOR.$MINOR-$ARCH + + #1-arch + docker tag $PROJECT $IMAGE_NAME:$MAJOR-$ARCH + docker push $IMAGE_NAME:$MAJOR-$ARCH + + #latest by arch + docker tag $PROJECT $IMAGE_NAME:$ARCH + docker push $IMAGE_NAME:$ARCH + fi diff --git a/Dockerfile-armhf b/Dockerfile-armhf new file mode 100644 index 0000000..e232bc0 --- /dev/null +++ b/Dockerfile-armhf @@ -0,0 +1,15 @@ +FROM arm32v7/python:slim + +ENV LIBRARY_PATH=/lib:/usr/lib + +ADD src/requirements.txt / +RUN apt-get update && apt-get install -y build-essential autoconf \ + && pip install --upgrade pip && pip install -r /requirements.txt \ + && apt-get remove -y build-essential autoconf \ + && apt-get autoremove -y \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app +COPY src /app + +CMD ["python3", "-u", "/app/main.py"] \ No newline at end of file diff --git a/Dockerfile-rpi b/Dockerfile-rpi deleted file mode 100644 index 102cf06..0000000 --- a/Dockerfile-rpi +++ /dev/null @@ -1,11 +0,0 @@ -FROM alpine-python:latest - -ENV LIBRARY_PATH=/lib:/usr/lib - -ADD src/requirements.txt / -RUN pip install --upgrade pip && pip install -r /requirements.txt - -WORKDIR /app -COPY src /app - -CMD ["python3", "-u", "/app/main.py"] diff --git a/README.md b/README.md index ccd3bdc..9e6b757 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,12 @@ will turn on plug/heater and translate devices state from gateway: "home/plug/heater/status" on ``` +## Architecture +Docker image support following architectures (you must choose your architecture in docker-compose): +- armhf (raspberry pi 3, arm32v7) +- i386 (x86 pc) +- x64 (x64 pc) + ## Config Edit file config/config-sample.yaml and rename it to config/config.yaml @@ -21,7 +27,7 @@ Edit file config/config-sample.yaml and rename it to config/config.yaml Sample docker-compose.yaml file for user: ``` aqara: - image: monster1025/aqara-mqtt + image: monster1025/aqara-mqtt:1-armhf container_name: aqara volumes: - "./config:/app/config" diff --git a/config/config-sample.yaml b/config/config-sample.yaml index 5ee8909..5db843c 100644 --- a/config/config-sample.yaml +++ b/config/config-sample.yaml @@ -4,6 +4,8 @@ mqtt: username: username password: passw0rd prefix: home + #send report as json + json: False gateway: password: passw0rd diff --git a/docker-compose.yaml b/docker-compose.yaml index 6c55f82..be7edd1 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,6 +1,5 @@ aqara: - build: . - dockerfile: Dockerfile-rpi + image: monster1025/aqara-mqtt:1-armhf container_name: aqara volumes: - "./config:/app/config" diff --git a/src/mqtt.py b/src/mqtt.py index 328db4b..5684de5 100644 --- a/src/mqtt.py +++ b/src/mqtt.py @@ -8,6 +8,7 @@ import json _LOGGER = logging.getLogger(__name__) class Mqtt: + event_based_sensors = ["switch", "cube"] username = "" password = "" server = "localhost" @@ -38,6 +39,7 @@ class Mqtt: self.server = mqttConfig.get("server", "localhost") self.port = mqttConfig.get("port", 1883) self.prefix = mqttConfig.get("prefix", "home") + self.json = mqttConfig.get("json", False) self._queue = Queue() self._threads = [] @@ -72,8 +74,6 @@ class Mqtt: model = sidprops.get("model",model) sid = sidprops.get("name",sid) - # _LOGGER.info("data is " + format(data)) - PATH_FMT = self.prefix + "/{model}/{sid}/{prop}" for key, value in data.items(): # fix for latest motion value if (model == "motion" and key == "no_motion"): @@ -82,18 +82,29 @@ class Mqtt: if (model == "magnet" and key == "no_close"): key="status" value="open" - # do not retain event-based sensors (like switches and cubes). - if (model in ["switch", "cube"]): + if (model in self.event_based_sensors): retain = False - # fix for rgb format if (key == "rgb" and self._is_int(value)): value = self._color_xiaomi_to_rgb(value) - topic = PATH_FMT.format(model=model, sid=sid, prop=key) - _LOGGER.info("Publishing message to topic " + topic + ": " + str(value) + ".") - self._client.publish(topic, payload=value, qos=0, retain=retain) + if self.json == True: + PATH_FMT = self.prefix + "/{model}/{sid}/json" + topic = PATH_FMT.format(model=model, sid=sid) + values = {} + values['sid'] = sid + for key, value in data.items(): + values[key] = value + jsondata = json.dumps(values) + _LOGGER.info("Publishing message to topic " + topic + ": " + str(jsondata) + ".") + self._client.publish(topic, payload=jsondata, qos=0, retain=retain) + else: + for key, value in data.items(): + PATH_FMT = self.prefix + "/{model}/{sid}/{prop}" + topic = PATH_FMT.format(model=model, sid=sid, prop=key) + _LOGGER.info("Publishing message to topic " + topic + ": " + str(value) + ".") + self._client.publish(topic, payload=value, qos=0, retain=retain) def _mqtt_on_connect(self, client, userdata, rc, unk): _LOGGER.info("Connected to mqtt server.")