Code rewrited

This commit is contained in:
Your Name
2017-01-13 16:09:55 +03:00
parent b9524b348b
commit e2e9d45cd7
11 changed files with 337 additions and 117 deletions
+5 -3
View File
@@ -1,6 +1,8 @@
#docker-compose file, because it contains passwords #config file, because it contains passwords
docker-compose.yaml config.yml
docker-compose.yml config.yaml
config/config.yaml
config/config.yml
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
+11
View File
@@ -0,0 +1,11 @@
FROM monster1025/alpine86-python
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 ["python", "-u", "/app/main.py"]
-1
View File
@@ -8,5 +8,4 @@ RUN pip install --upgrade pip && pip install -r /requirements.txt
WORKDIR /app WORKDIR /app
COPY src /app COPY src /app
EXPOSE 5000
CMD ["python", "-u", "/app/main.py"] CMD ["python", "-u", "/app/main.py"]
+23 -12
View File
@@ -3,23 +3,34 @@
Works with Yeelight WiFi bulbs (color and monochrome). Works with Yeelight WiFi bulbs (color and monochrome).
You need to edit you bulb IPs in main.py (not yet fixed). You need to edit you bulb IPs in main.py (not yet fixed).
Sample docker-compose.yml file: You need to activate developer mode (http://forum.yeelight.com/t/trying-to-enable-developer-mode-with-yeelight-app-lamp-always-offline/137)
Bridge accept following MQTT set:
``` ```
yeelight: "home/light/main-color/status/set" -> on
build: .
container_name: yeelight
environment:
- MQTT_SERVER=192.168.1.93
- MQTT_USER=mqtt_user
- MQTT_PASS=passw0rd
restart: always
``` ```
will turn on light and translate devices state from gateway:
``` ```
docker-compose build && docker-compose up -d "home/light/main-color/status" on
"home/light/main-color/ct" 3500
"home/light/main-color/bright" 3
"home/light/main-color/rgb" 1247743
```
## Config
Edit file config/config-sample.yaml and rename it to config/config.yaml
## Docker-Compose
Sample docker-compose.yaml file for user:
```
yeelight:
image: "monster1025/yeelight-mqtt"
container_name: yeelight
volumes:
- "./config:/app/config"
restart: always
``` ```
## Known bugs: ## Known bugs:
- Lamp's alive status updated only at startup. - Lamp's alive status updated only at startup.
- IP-Name list in main.py
- Bad code style =(
+21
View File
@@ -0,0 +1,21 @@
mqtt:
server: 127.0.0.1
port: 1883
username: mqttuser
password: passw0rd
prefix: home
sids:
# lamps
192.168.1.51:
model: light
name: main-mono
192.168.1.54:
model: light
name: main-color
192.168.1.52:
model: light
name: kitchen
192.168.1.53:
model: light
name: hall
+7
View File
@@ -0,0 +1,7 @@
yeelight:
build: .
dockerfile: Dockerfile-i386
container_name: yeelight
volumes:
- "./config:/app/config"
restart: always
+12 -10
View File
@@ -1,20 +1,22 @@
import pyyeelight import pyyeelight
import os import logging
import time
import threading _LOGGER = logging.getLogger(__name__)
class LightBulbState: class LightBulbState:
bright = 0 bright = 0
color_temperature = 0 color_temperature = 0
status = "off" status = "off"
rgb = 0 rgb = 0
model = ""
ip = "" ip = ""
name = "" name = ""
yeelight = None # yeelight object yeelight = None # yeelight object
def __init__(self, ip, yeelightObj): def __init__(self, ip, model, yeelightObj):
self.yeelight = yeelightObj self.yeelight = yeelightObj
self.model = model
self.name = yeelightObj.__name__ self.name = yeelightObj.__name__
self.ip = ip self.ip = ip
@@ -42,23 +44,23 @@ class LightBulbState:
try: try:
if (param == 'status'): if (param == 'status'):
if (value == "on"): if (value == "on"):
print("Turning on bulb", self.name) _LOGGER.info("Turning on bulb: " + self.name)
self.yeelight.turn_on() self.yeelight.turn_on()
if (value == "off"): if (value == "off"):
print("Turning off bulb", self.name) _LOGGER.info("Turning off bulb: " + self.name)
self.yeelight.turn_off() self.yeelight.turn_off()
if (param == 'bright' and self.is_int(value)): if (param == 'bright' and self.is_int(value)):
print("Setting brightness of bulb", self.name, 'to', value) _LOGGER.info("Setting brightness of bulb " + self.name + " to " + str(value))
self.yeelight.set_brightness(int(value)) self.yeelight.set_brightness(int(value))
if (param == 'ct' and self.is_int(value)): if (param == 'ct' and self.is_int(value)):
print("Setting temperature of bulb", self.name, 'to', value) _LOGGER.info("Setting temperature of bulb " + self.name + " to " + str(value))
self.yeelight.set_color_temperature(int(value)) self.yeelight.set_color_temperature(int(value))
if (param == 'rgb' and self.is_int(value)): if (param == 'rgb' and self.is_int(value)):
intval = int(value) intval = int(value)
Blue = intval & 255 Blue = intval & 255
Green = (intval >> 8) & 255 Green = (intval >> 8) & 255
Red = (intval >> 16) & 255 Red = (intval >> 16) & 255
print("Setting rgb of bulb", self.name, 'to', value) _LOGGER.info("Setting rgb of bulb", self.name, 'to', value)
self.yeelight.set_rgb_color(Red, Green, Blue) self.yeelight.set_rgb_color(Red, Green, Blue)
except Exception as e: except Exception as e:
print ('Error while set value of bulb', self.name , ' error:', e) _LOGGER.error('Error while set value of bulb ' + self.name + ' error:', e)
+86 -87
View File
@@ -1,113 +1,112 @@
import pyyeelight import logging
import paho.mqtt.client as mqtt
import os
import time import time
import threading import threading
import os
import json
import pyyeelight
from lightbulbstate import LightBulbState from lightbulbstate import LightBulbState
MQTT_SERVER = os.getenv('MQTT_SERVER', "") #mine
MQTT_PORT = os.getenv('MQTT_PORT', 1883) import mqtt
MQTT_USER = os.getenv('MQTT_USER', "") import yamlparser
MQTT_PASS = os.getenv('MQTT_PASS', "")
QUERY_TIME = os.getenv('QUERY_TIME', 3) logging.basicConfig(level=logging.INFO)
PREFIX = os.getenv('PREFIX', 'home') _LOGGER = logging.getLogger(__name__)
QUERY_TIME = 2
ips = dict({
'192.168.1.51':'main-mono',
'192.168.1.54':'main-color',
'192.168.1.52':'kitchen',
'192.168.1.53':'hall'
})
bulbs=[] bulbs=[]
processNow = False processNow = False
PATH_FMT = PREFIX + "/{model}/{sid}/{prop}" # short_id or sid ? def init_lamps(config):
if config is None:
raise "Config is None."
sids = config.get("sids", "None")
if sids is None:
raise "Config -> sids is None."
def prepare_mqtt():
print("Connecting to MQTT server", MQTT_SERVER, ":", MQTT_PORT, "with username", MQTT_USER,":",MQTT_PASS)
client = mqtt.Client()
if (MQTT_USER != "" and MQTT_PASS != ""):
client.username_pw_set(MQTT_USER, MQTT_PASS)
client.connect(MQTT_SERVER, MQTT_PORT, 60)
return client
def push_data(client, model, sid, data):
for key, value in data.items():
path = PATH_FMT.format(model=model,
sid=sid,
prop=key)
client.publish(path, payload=value, qos=0, retain=True)
def init_lamps():
lamps=[] lamps=[]
for ip in ips: # sid is IP-address
for sid in sids:
if (sid is None):
continue
try: try:
yeelight = pyyeelight.YeelightBulb(ip) data = sids[sid]
yeelight.__name__ = ips[ip] #add name yeelight = pyyeelight.YeelightBulb(sid)
bulb = LightBulbState(ip, yeelight) name = data.get("name", sid)
model = data.get("model", "light")
yeelight.__name__ = name #add name
bulb = LightBulbState(sid, model, yeelight)
bulb.update_properties() bulb.update_properties()
lamps.append(bulb) lamps.append(bulb)
except Exception as e: except Exception as e:
print('Connection to ', str(ip) , ' error:', str(e)) _LOGGER.error('Connection to ', str(sid) , ' error:', str(e))
return lamps return lamps
def wait():
def refresh_bulb_states(data_callback):
for bulb in bulbs:
try:
hashold = bulb.hash()
bulb.update_properties(force=True)
hashnew = bulb.hash()
# print(str(bulb.name),hashold, hashnew)
if (hashold != hashnew):
print("!!!! ", bulb.name, ":", hashold, "->", hashnew)
if data_callback is not None:
data = {'status':bulb.status, 'ct':bulb.color_temperature, 'bright':bulb.bright, 'rgb':bulb.rgb}
data_callback("lamp", bulb.name, data)
except Exception as e:
print('Connection to ', str(bulb.name) , ' error:', str(e))
def on_mqtt_message(client, userdata, msg):
global processNow global processNow
print(msg.topic+" "+str(msg.payload))
parts = msg.topic.split("/")
if (len(parts) != 5):
return
name = parts[2] #name part
param = parts[3] #param part
value = (msg.payload).decode('utf-8')
for bulb in bulbs:
if (bulb.name != name):
continue
bulb.process_command(param, value)
processNow=True
def on_connect(client, userdata, rc):
client.subscribe(PREFIX + "/lamp/+/+/set")
def refresh_loop(client):
global processNow
cb = lambda m, s, d: push_data(client, m, s, d)
while True:
refresh_bulb_states(cb)
for x in range(1,10): for x in range(1,10):
if (processNow): if (processNow):
processNow=False processNow=False
break break
time.sleep(QUERY_TIME/10) time.sleep(QUERY_TIME/10)
if __name__ == "__main__": def process_lamp_states(client):
client = prepare_mqtt() global bulbs
bulbs = init_lamps() while True:
client.on_message = on_mqtt_message wait();
client.on_connect = on_connect try:
for bulb in bulbs:
hashold = bulb.hash()
bulb.update_properties(force=True)
hashnew = bulb.hash()
# _LOGGER.debug(str(bulb.name) + " ===> " + hashold + "-" + hashnew)
#start thread for lamp refresh loop if (hashold != hashnew):
t1 = threading.Thread(target=refresh_loop, args=[client]) _LOGGER.info("!!!! " + bulb.name + ":" + hashold + "->" + hashnew)
data = {'status':bulb.status, 'ct':bulb.color_temperature, 'bright':bulb.bright, 'rgb':bulb.rgb}
client.publish(bulb.model, bulb.name, data)
except Exception as e:
_LOGGER.error('Error while sending from gateway to mqtt: ', str(e))
def process_mqtt_messages(client):
global processNow, bulbs
while True:
try:
data = client._queue.get()
_LOGGER.debug("data from mqtt: " + format(data))
sid = data.get("sid", None)
param = data.get("param", None)
value = data.get("value", None)
for bulb in bulbs:
if (bulb.ip != sid):
continue
bulb.process_command(param, value)
processNow=True
client._queue.task_done()
except Exception as e:
_LOGGER.error('Error while sending from mqtt to gateway: ', str(e))
if __name__ == "__main__":
_LOGGER.info("Loading config file...")
config=yamlparser.load_yaml('config/config.yaml')
_LOGGER.info("Init mqtt client.")
client = mqtt.Mqtt(config)
client.connect()
#only this devices can be controlled from MQTT
client.subscribe("light", "+", "+", "set")
bulbs = init_lamps(config)
t1 = threading.Thread(target=process_lamp_states, args=[client])
t1.daemon = True
t1.start() t1.start()
# and process mqtt messages in this thread t2 = threading.Thread(target=process_mqtt_messages, args=[client])
client.loop_forever() t2.daemon = True
t2.start()
while True:
time.sleep(10)
+142
View File
@@ -0,0 +1,142 @@
import paho.mqtt.client as mqtt
import os
import logging
from queue import Queue
from threading import Thread
_LOGGER = logging.getLogger(__name__)
class Mqtt:
username = ""
password = ""
server = "localhost"
port = 1883
prefix = "home"
_client = None
_sids = None
_queue = None
_threads = None
def __init__(self, config):
if (config == None):
raise "Config is null"
#load sids dictionary
self._sids = config.get("sids", None)
if (self._sids == None):
self._sids = dict({})
#load mqtt settings
mqttConfig = config.get("mqtt", None)
if (mqttConfig == None):
raise "Config mqtt section is null"
self.username = mqttConfig.get("username", "")
self.password = mqttConfig.get("password", "")
self.server = mqttConfig.get("server", "localhost")
self.port = mqttConfig.get("port", 1883)
self.prefix = mqttConfig.get("prefix", "home")
self._queue = Queue()
self._threads = []
def connect(self):
_LOGGER.info("Connecting to MQTT server " + self.server + ":" + str(self.port) + " with username (" + self.username + ":" + self.password + ")")
self._client = mqtt.Client()
if (self.username != "" and self.password != ""):
self._client.username_pw_set(self.username, self.password)
self._client.on_message = self._mqtt_process_message
self._client.on_connect = self._mqtt_on_connect
self._client.connect(self.server, self.port, 60)
#run message processing loop
t1 = Thread(target=self._mqtt_loop)
t1.start()
self._threads.append(t1)
def subscribe(self, model="+", name="+", prop="+", command="set"):
topic = self.prefix + "/" + model + "/" + name + "/" + prop + "/" + command
_LOGGER.info("Subscibing to " + topic + ".")
self._client.subscribe(topic)
def publish(self, model, sid, data, retain=True):
sidprops = self._sids.get(sid, None)
if (sidprops != None):
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"):
key="status"
value="no_motion"
# fix for rgb format
# if (key == "rgb" and self._is_int(value)):
# intval = int(value)
# blue = (intval) & 255
# green = (intval >> 8) & 255
# red = (intval >> 16) & 255
# value = str(red)+","+str(green)+","+str(blue)
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.")
def _mqtt_process_message(self, client, userdata, msg):
_LOGGER.info("Processing message in " + str(msg.topic) + ": " + str(msg.payload) + ".")
parts = msg.topic.split("/")
if (len(parts) != 5):
return
model = parts[1]
query_sid = parts[2] #sid or name part
param = parts[3] #param part
value = (msg.payload).decode('utf-8')
if self._is_int(value):
value = int(value)
name = "" # we will find it next
sid = query_sid
for current_sid in self._sids:
if (current_sid == None):
continue
sidprops = self._sids.get(current_sid, None)
if sidprops == None:
continue
sidname = sidprops.get("name", current_sid)
sidmodel = sidprops.get("model", "")
if (sidname == query_sid and sidmodel == model):
sid = current_sid
name = sidname
break
else:
_LOGGER.debug(sidmodel + "-" + sidname + " is not " + model + "-" + query_sid + ".")
continue
# fix for rgb format
if (param == "rgb" and "," in str(value)):
arr = value.split(",")
r = int(arr[0])
g = int(arr[1])
b = int(arr[2])
value = int('%02x%02x%02x%02x' % (255, r, g, b), 16)
data = {'sid': sid, 'model': model, 'name': name, 'param':param, 'value':value}
# put in process queuee
self._queue.put(data)
def _mqtt_loop(self):
_LOGGER.info("Starting mqtt loop.")
self._client.loop_forever()
def _is_int(self, x):
try:
tmp = int(x)
return True
except Exception as e:
return False
+2
View File
@@ -1,2 +1,4 @@
paho-mqtt paho-mqtt
pyyeelight==1.0-beta pyyeelight==1.0-beta
pyyaml
pycrypto
+24
View File
@@ -0,0 +1,24 @@
import yaml
import logging
_LOGGER = logging.getLogger(__name__)
def load_yaml(file):
try:
stram = open(file, "r")
yaml_data = yaml.load(stram)
return yaml_data
except Exception as e:
raise
_LOGGER.error("Can't load yaml with sids %r (%r)" % (file, e))
def get_gateway_password(config, ip=""):
if (config == None):
raise "Config is null"
configGateway = config.get("gateway", None)
if (configGateway == None):
raise "Config gateway is null"
password = configGateway.get("password", None)
if (password == None):
raise "Config gateway passowrd is null"
return password