├── blebox_switchbox.png ├── blebox_wlightbox1.png ├── blebox_wlightbox2.png ├── README.md ├── .gitignore ├── switch └── blebox_switchbox.py └── light └── blebox_wlightbox.py /blebox_switchbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zen/hass_blebox/master/blebox_switchbox.png -------------------------------------------------------------------------------- /blebox_wlightbox1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zen/hass_blebox/master/blebox_wlightbox1.png -------------------------------------------------------------------------------- /blebox_wlightbox2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zen/hass_blebox/master/blebox_wlightbox2.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Home Assistant Custom Components 2 | 3 | ## How to load custom components 4 | https://developers.home-assistant.io/docs/en/creating_component_loading.html 5 | 6 | ## Blebox switchBox(D) 7 | __To enable this switch, add the following lines to your configuration.yaml file:__ 8 | ``` 9 | switch: 10 | - platform: blebox_switchbox 11 | host: IP_ADDRESS 12 | ``` 13 | __Configuration variables:__ 14 | * __host__ (*Required*): The IP address of your switchBox(D), eg. 192.168.1.32 15 | * __name__ (*Optional*): The name to use when displaying this switch. If not set, will be used relay name from the device 16 | * __relay__ (*Optional*): The number of the relay. Default is 0, for switchBoxD you can set 0 or 1 17 | 18 | 19 | 20 | ## Blebox wLightBox 21 | __To enable this switch, add the following lines to your configuration.yaml file:__ 22 | ``` 23 | light: 24 | - platform: blebox_wlightbox 25 | host: IP_ADDRESS 26 | ``` 27 | __Configuration variables:__ 28 | * __host__ (*Required*): The IP address of your wLightBox, eg. 192.168.1.32 29 | * __name__ (*Optional*): The name to use when displaying this switch. If not set, will be used the device name 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /switch/blebox_switchbox.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import voluptuous as vol 3 | import json 4 | import asyncio 5 | import async_timeout 6 | 7 | from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) 8 | from homeassistant.const import (CONF_NAME, CONF_HOST, CONF_TIMEOUT, STATE_OFF, STATE_ON) 9 | import homeassistant.helpers.config_validation as cv 10 | from homeassistant.helpers.aiohttp_client import async_get_clientsession 11 | 12 | LOGGING = logging.getLogger(__name__) 13 | CONF_RELAY = 'relay' 14 | DEFAULT_NAME = 'Blebox switchBox' 15 | DEFAULT_RELAY = 0 16 | DEFAULT_TIMEOUT = 10 17 | 18 | PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 19 | vol.Required(CONF_HOST): cv.string, 20 | vol.Optional(CONF_NAME): cv.string, 21 | vol.Optional(CONF_RELAY, default=DEFAULT_RELAY): cv.positive_int, 22 | vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, 23 | }) 24 | 25 | @asyncio.coroutine 26 | def async_setup_platform(hass, config, async_add_devices, discovery_info=None): 27 | name = config.get(CONF_NAME) 28 | host = config.get(CONF_HOST) 29 | relay = config.get(CONF_RELAY) 30 | timeout = config.get(CONF_TIMEOUT) 31 | 32 | switch = BleboxSwitchBoxSwitch(name, host, relay, timeout) 33 | yield from switch.async_relay_init(hass) 34 | async_add_devices([switch]) 35 | 36 | class BleboxSwitchBoxSwitch(SwitchDevice): 37 | 38 | def __init__(self, name, host, relay, timeout): 39 | self._name = name 40 | self._state = 0 41 | self._host = host 42 | self._relay = relay 43 | self._timeout = timeout 44 | self._available = False 45 | 46 | @property 47 | def available(self): 48 | return self._available 49 | 50 | @property 51 | def name(self): 52 | return self._name 53 | 54 | @property 55 | def state(self): 56 | return self._state 57 | 58 | @state.setter 59 | def state(self, state): 60 | self._state = STATE_ON if state else STATE_OFF 61 | 62 | @property 63 | def is_on(self): 64 | return self._state == STATE_ON 65 | 66 | @asyncio.coroutine 67 | def async_turn_on(self, **kwargs): 68 | yield from self.set_relay_state(1) 69 | 70 | @asyncio.coroutine 71 | def async_turn_off(self, **kwargs): 72 | yield from self.set_relay_state(0) 73 | 74 | @asyncio.coroutine 75 | def async_relay_init(self, hass): 76 | relay_info = yield from self.async_update_relay_info(hass) 77 | 78 | if not self._name: 79 | self._name = relay_info['name'] if relay_info else DEFAULT_NAME 80 | 81 | return relay_info 82 | 83 | @asyncio.coroutine 84 | def async_update_relay_info(self, hass): 85 | relay_info = yield from self.get_relay_info(hass) 86 | 87 | if relay_info: 88 | self.state = relay_info['state'] 89 | self._available = True 90 | else: 91 | self.state = 0 92 | self._available = False 93 | 94 | return relay_info 95 | 96 | @asyncio.coroutine 97 | def async_update(self): 98 | yield from self.async_update_relay_info(self.hass) 99 | 100 | @asyncio.coroutine 101 | def set_relay_state(self, state): 102 | websession = async_get_clientsession(self.hass) 103 | resource = 'http://%s/api/relay/set' % self._host 104 | payload = '{"relays": [{"relay": %i, "state": %i}]}' % (self._relay, state) 105 | 106 | try: 107 | with async_timeout.timeout(self._timeout, loop=self.hass.loop): 108 | req = yield from getattr(websession, 'post')(resource, data=bytes(payload, 'utf-8')) 109 | text = yield from req.text() 110 | relay_info = json.loads(text)['relays'][self._relay] 111 | return relay_info 112 | except: 113 | return None 114 | 115 | @asyncio.coroutine 116 | def get_relay_info(self, hass): 117 | websession = async_get_clientsession(hass) 118 | resource = 'http://%s/api/relay/state' % self._host 119 | 120 | try: 121 | with async_timeout.timeout(self._timeout, loop=hass.loop): 122 | req = yield from websession.get(resource) 123 | text = yield from req.text() 124 | relay_info = json.loads(text)['relays'][self._relay] 125 | return relay_info 126 | except: 127 | return None 128 | -------------------------------------------------------------------------------- /light/blebox_wlightbox.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import voluptuous as vol 3 | import json 4 | import asyncio 5 | import async_timeout 6 | import homeassistant.helpers.config_validation as cv 7 | from homeassistant.components.light import ( 8 | ATTR_BRIGHTNESS, ATTR_EFFECT, ATTR_HS_COLOR, 9 | ATTR_WHITE_VALUE, SUPPORT_BRIGHTNESS, SUPPORT_EFFECT, 10 | SUPPORT_COLOR, SUPPORT_WHITE_VALUE, Light, PLATFORM_SCHEMA) 11 | from homeassistant.const import (CONF_NAME, CONF_HOST, CONF_TIMEOUT, STATE_OFF, STATE_ON) 12 | from homeassistant.helpers.aiohttp_client import async_get_clientsession 13 | from homeassistant.util.color import (color_hsb_to_RGB, color_hsv_to_RGB, color_RGB_to_hsv, rgb_hex_to_rgb_list) 14 | 15 | LOGGING = logging.getLogger(__name__) 16 | LIGHT_EFFECT_LIST = ['BRAK', 'ŚCIEMNIANIE', 'RGB', 'POLICJA', 'RELAKS', 'STROBOSKOP'] 17 | SUPPORTED_FEATURES_MONO = (SUPPORT_BRIGHTNESS) 18 | SUPPORTED_FEATURES_RGB = (SUPPORT_BRIGHTNESS | SUPPORT_EFFECT | SUPPORT_COLOR) 19 | SUPPORTED_FEATURES_RGBW = (SUPPORT_BRIGHTNESS | SUPPORT_EFFECT | SUPPORT_COLOR | SUPPORT_WHITE_VALUE) 20 | DEFAULT_NAME = 'Blebox wLightBox' 21 | DEFAULT_RELAY = 0 22 | DEFAULT_TIMEOUT = 10 23 | 24 | PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 25 | vol.Required(CONF_HOST): cv.string, 26 | vol.Optional(CONF_NAME): cv.string, 27 | vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, 28 | }) 29 | 30 | @asyncio.coroutine 31 | def async_setup_platform(hass, config, async_add_devices, discovery_info=None): 32 | name = config.get(CONF_NAME) 33 | host = config.get(CONF_HOST) 34 | timeout = config.get(CONF_TIMEOUT) 35 | 36 | light = BleboxWlightBoxLight(name, host, timeout) 37 | yield from light.async_device_init(hass) 38 | async_add_devices([light]) 39 | 40 | class BleboxWlightBoxLight(Light): 41 | def __init__(self, name, host, timeout): 42 | self._name = name 43 | self._host = host 44 | self._timeout = timeout 45 | self._state = False 46 | self._hs_color = (0, 0) 47 | self._brightness = 255 48 | self._white = 0 49 | self._effect_list = LIGHT_EFFECT_LIST 50 | self._effect = 'BRAK' 51 | self._color_mode = 1 52 | self._available = False 53 | 54 | @property 55 | def should_poll(self): 56 | return True 57 | 58 | @property 59 | def name(self): 60 | return self._name 61 | 62 | @property 63 | def state(self): 64 | return self._state 65 | 66 | @state.setter 67 | def state(self, state): 68 | self._state = STATE_ON if state else STATE_OFF 69 | 70 | @property 71 | def is_on(self): 72 | return self._state == STATE_ON 73 | 74 | @property 75 | def available(self): 76 | return self._available 77 | 78 | @property 79 | def brightness(self): 80 | return self._brightness 81 | 82 | @property 83 | def hs_color(self): 84 | return self._hs_color 85 | 86 | @property 87 | def white_value(self): 88 | return self._white 89 | 90 | @property 91 | def effect_list(self): 92 | return self._effect_list 93 | 94 | @property 95 | def effect(self): 96 | return self._effect 97 | 98 | @property 99 | def supported_features(self): 100 | return SUPPORTED_FEATURES_RGBW if self._color_mode == 1 else (SUPPORTED_FEATURES_RGB if self._color_mode == 2 else SUPPORTED_FEATURES_MONO) 101 | 102 | @asyncio.coroutine 103 | def async_device_init(self, hass): 104 | device_info = yield from self.async_update_device_info(hass) 105 | 106 | if not self._name: 107 | self._name = device_info['device']['deviceName'] if device_info else DEFAULT_NAME 108 | 109 | return device_info 110 | 111 | @asyncio.coroutine 112 | def async_update_device_info(self, hass): 113 | 114 | device_info = None 115 | 116 | try: 117 | device_info = yield from self.get_device_info(hass) 118 | self._available = True 119 | self._color_mode = device_info['rgbw']['colorMode'] 120 | 121 | current_color = device_info['rgbw']['desiredColor'] 122 | effect_id = device_info['rgbw']['effectID'] 123 | except: 124 | self._available = False 125 | current_color = '00000000' 126 | effect_id = 0 127 | 128 | if current_color != '00000000': 129 | self.state = True 130 | 131 | rgb = current_color[:6] 132 | rgb = rgb_hex_to_rgb_list(rgb) 133 | hsv = color_RGB_to_hsv(*tuple(rgb)) 134 | 135 | self._brightness = int((hsv[2]*255)/100) 136 | self._white = int(current_color[6:8], 16) 137 | self._hs_color = hsv[0:2] 138 | self._effect = LIGHT_EFFECT_LIST[effect_id] 139 | else: 140 | self.state = False 141 | 142 | return device_info 143 | 144 | @asyncio.coroutine 145 | def async_update(self): 146 | yield from self.async_update_device_info(self.hass) 147 | 148 | @asyncio.coroutine 149 | def async_turn_on(self, **kwargs): 150 | if ATTR_HS_COLOR in kwargs: 151 | self._hs_color = kwargs[ATTR_HS_COLOR] 152 | 153 | if ATTR_BRIGHTNESS in kwargs: 154 | self._brightness = kwargs[ATTR_BRIGHTNESS] 155 | 156 | if ATTR_WHITE_VALUE in kwargs: 157 | self._white = kwargs[ATTR_WHITE_VALUE] 158 | 159 | if ATTR_EFFECT in kwargs: 160 | self._effect = kwargs[ATTR_EFFECT] 161 | 162 | if self._color_mode == 2: #RGB 163 | self._white = 0 164 | elif self._color_mode == 3: #MONO 165 | self._white = self._brightness 166 | self._hs_color = (0, 0) 167 | 168 | rgb = color_hsv_to_RGB(self._hs_color[0], self._hs_color[1], (self._brightness/255)*100) 169 | rgbw = '{0:02x}{1:02x}{2:02x}{3:02x}'.format(rgb[0], rgb[1], rgb[2], self._white) 170 | 171 | yield from self.set_device_color(rgbw, self._effect) 172 | 173 | @asyncio.coroutine 174 | def async_turn_off(self): 175 | yield from self.set_device_color('00000000') 176 | 177 | @asyncio.coroutine 178 | def set_device_color(self, color, effect = 'BRAK'): 179 | websession = async_get_clientsession(self.hass) 180 | resource = 'http://%s/api/rgbw/set' % self._host 181 | effect_id = LIGHT_EFFECT_LIST.index(effect) 182 | payload = '{"rgbw": {"desiredColor": "%s", "effectID": %i}}' % (color, effect_id) 183 | 184 | try: 185 | with async_timeout.timeout(self._timeout, loop=self.hass.loop): 186 | req = yield from getattr(websession, 'post')(resource, data=bytes(payload, 'utf-8')) 187 | text = yield from req.text() 188 | return json.loads(text)['rgbw'] 189 | except: 190 | return None 191 | 192 | @asyncio.coroutine 193 | def get_device_info(self, hass): 194 | websession = async_get_clientsession(hass) 195 | resource = 'http://%s/api/device/state' % self._host 196 | 197 | try: 198 | with async_timeout.timeout(self._timeout, loop=hass.loop): 199 | req = yield from websession.get(resource) 200 | text = yield from req.text() 201 | device_info = json.loads(text) 202 | device = device_info['device'] 203 | return device_info 204 | except: 205 | return None 206 | --------------------------------------------------------------------------------