From 7dc2fa9497a81c9d56747ba3ecb7222f51c3d56d Mon Sep 17 00:00:00 2001 From: CoprDistGit Date: Thu, 9 Mar 2023 16:10:32 +0000 Subject: automatic import of python-pywizlight --- .gitignore | 1 + python-pywizlight.spec | 1072 ++++++++++++++++++++++++++++++++++++++++++++++++ sources | 1 + 3 files changed, 1074 insertions(+) create mode 100644 python-pywizlight.spec create mode 100644 sources diff --git a/.gitignore b/.gitignore index e69de29..9a73e59 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +/pywizlight-0.5.14.tar.gz diff --git a/python-pywizlight.spec b/python-pywizlight.spec new file mode 100644 index 0000000..7ad5be9 --- /dev/null +++ b/python-pywizlight.spec @@ -0,0 +1,1072 @@ +%global _empty_manifest_terminate_build 0 +Name: python-pywizlight +Version: 0.5.14 +Release: 1 +Summary: A python connector for WiZ light bulbs (e.g SLV Play) +License: MIT +URL: https://github.com/sbidy/pywizlight +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/e9/7b/c7f4b79e88c0ce565c11f90df6a2a714aa1d8154e615217ea77254b0e240/pywizlight-0.5.14.tar.gz +BuildArch: noarch + +Requires: python3-click + +%description +![Upload Python Package](https://github.com/sbidy/pywizlight/workflows/Upload%20Python%20Package/badge.svg) + + +[![All Contributors](https://img.shields.io/badge/all_contributors-18-orange.svg?style=flat-square)](#contributors-) + + +![Update Docs](https://github.com/sbidy/pywizlight/workflows/Update%20Docs/badge.svg) +[![Lint](https://github.com/sbidy/pywizlight/actions/workflows/lint.yml/badge.svg)](https://github.com/sbidy/pywizlight/actions/workflows/lint.yml) +[![codecov][code-cover-shield]][code-coverage] + + +# pywizlight + +A Python connector for [WiZ](https://www.wizconnected.com/en/consumer/) devices. + +## Install + +```bash +pip install pywizlight +``` + +**Note:** Requires Python version `>=3.7`. + +### Fedora/CentOS + +On a Fedora-based system or on a CentOS/RHEL 8 machine which has EPEL enabled, as +[`pywizlight`](https://src.fedoraproject.org/rpms/python-pywizlight) is present in the +Fedora Package Collection. + +```bash +sudo dnf -y install python3-pywizlight +``` + +### NixOS + +For NixOS and Nix the latest release of `pywizlight` is usually available in the [`unstable`](https://search.nixos.org/packages?channel=unstable&query=pywizlight) +channel. Stable releases might ship older versions of `pywizlight`. + +```bash +nix-env -iA nixos.python37Packages.pywizlight +``` + +## Contributors + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Seth Nickell

πŸ’»

David Zurow

πŸ“–

Eduardo Ibanez

πŸ“–

Angad Singh

πŸ’»

Fabian Affolter

πŸ“– πŸ’»

Henry Ruhs

πŸ’»

Alberto Panu

πŸ’»

skitterrusty

πŸ’»

Mathias Roth

πŸ’»

durnezj

πŸ’»

NoΓ«lle

πŸ“–

Patrick Kelley

πŸ“–

Ellis Michael

πŸ’»

Gughan Ravikumar

πŸ’»

Charlotte

πŸ’»

Aarni Koskela

πŸ’»

UH-60

πŸ’»

J. Nick Koston

πŸ’»
+ + + + + + +## Discover bulbs via CLI + +To find bulbs via cli you can use the following: +```bash +python -m pywizlight.cli discover +``` + +## Example + +```python +import asyncio + +from pywizlight import wizlight, PilotBuilder, discovery + +async def main(): + """Sample code to work with bulbs.""" + # Discover all bulbs in the network via broadcast datagram (UDP) + # function takes the discovery object and returns a list of wizlight objects. + bulbs = await discovery.discover_lights(broadcast_space="192.168.1.255") + # Print the IP address of the bulb on index 0 + print(f"Bulb IP address: {bulbs[0].ip}") + + # Iterate over all returned bulbs + for bulb in bulbs: + print(bulb.__dict__) + # Turn off all available bulbs + # await bulb.turn_off() + + # Set up a standard light + light = wizlight("192.168.1.27") + # Set up the light with a custom port + #light = wizlight("your bulb's IP address", port=12345) + + # The following calls need to be done inside an asyncio coroutine + # to run them from normal synchronous code, you can wrap them with + # asyncio.run(..). + + # Turn the light on into "rhythm mode" + await light.turn_on(PilotBuilder()) + # Set bulb brightness + await light.turn_on(PilotBuilder(brightness = 255)) + + # Set bulb brightness (with async timeout) + timeout = 10 + await asyncio.wait_for(light.turn_on(PilotBuilder(brightness = 255)), timeout) + + # Set bulb to warm white + await light.turn_on(PilotBuilder(warm_white = 255)) + + # Set RGB values + # red to 0 = 0%, green to 128 = 50%, blue to 255 = 100% + await light.turn_on(PilotBuilder(rgb = (0, 128, 255))) + + # Get the current color temperature, RGB values + state = await light.updateState() + print(state.get_colortemp()) + red, green, blue = state.get_rgb() + print(f"red {red}, green {green}, blue {blue}") + + # Start a scene + await light.turn_on(PilotBuilder(scene = 4)) # party + + # Get the name of the current scene + state = await light.updateState() + print(state.get_scene()) + + # Get the features of the bulb + bulb_type = await bulbs[0].get_bulbtype() + print(bulb_type.features.brightness) # returns True if brightness is supported + print(bulb_type.features.color) # returns True if color is supported + print(bulb_type.features.color_tmp) # returns True if color temperatures are supported + print(bulb_type.features.effect) # returns True if effects are supported + print(bulb_type.kelvin_range.max) # returns max kelvin in INT + print(bulb_type.kelvin_range.min) # returns min kelvin in INT + print(bulb_type.name) # returns the module name of the bulb + + # Turn the light off + await light.turn_off() + + # Do operations on multiple lights in parallel + #bulb1 = wizlight("") + #bulb2 = wizlight("") + # --- DEPRECATED in 3.10 see [#140](https://github.com/sbidy/pywizlight/issues/140) + # await asyncio.gather(bulb1.turn_on(PilotBuilder(brightness = 255)), + # bulb2.turn_on(PilotBuilder(warm_white = 255))) + # --- For >3.10 await asyncio.gather() from another coroutine + # async def turn_bulbs_on(bulb1, bulb2): + # await asyncio.gather(bulb1.turn_on(PilotBuilder(warm_white=255)), bulb2.turn_on(PilotBuilder(warm_white=255))) + # def main: + # asyncio.run(async turn_bulbs_on(bulb1, bulb2)) + +loop = asyncio.get_event_loop() +loop.run_until_complete(main()) +``` + +## CLI + +`wizlight` is a command-line tool to perform basic interactions with bulbs. + +```console +$ wizlight +Usage: wizlight [OPTIONS] COMMAND [ARGS]... + + Simple command-line tool to interact with Wizlight bulbs. + +Options: + --version Show the version and exit. + --help Show this message and exit. + +Commands: + discover Discover bulb in the local network. + off Turn the bulb off. + on Turn the bulb on. + state Get the current state from the given bulb. +``` + +## Discovery + +The discovery works with a UDP Broadcast request and collects all bulbs in the network. + +## Bulb paramters (UDP RAW) + +- **sceneId** - calls one of the predefined scenes (int from 1 to 32) [List of names in code](https://github.com/sbidy/pywizlight/blob/master/pywizlight/scenes.py) +- **speed** - sets the color changing speed in percent +- **dimming** - sets the dimmer of the bulb in percent +- **temp** - sets the color temperature in kelvins +- **r** - red color range 0-255 +- **g** - green color range 0-255 +- **b** - blue color range 0-255 +- **c** - cold white range 0-255 +- **w** - warm white range 0-255 +- **id** - the bulb id +- **state** - whether it's on or off +- **schdPsetId** - rhythm id of the room + +## Async I/O + +For async I/O this component uses Python's built-in asyncio DatagramTransport, which allows completely non-blocking UDP transport. + +## Classes + +`wizlight(ip)`: Creates an instance of a WiZ Light Bulb. Constructed with the IP addCancel changesress of the bulb. + +### Instance variables + +First you need to fetch the state by calling `light.updateState()`. +After that all states can be fetched from `light.state`, which is a `PilotParser` object. + +`PilotParser.get_brightness()`gets the value of the brightness 0-255 + +`PilotParser.get_rgb()` gets the rgbW color state of the bulb + +`PilotParser.get_colortemp()` gets the color temperature of the bulb + +`PilotParser.get_warm_white/get_cold_white()` gets the current warm/cold setting (not supported by original Philips Wiz bulbs) + +`PilotParser.get_scene()` gets the current scene name + +`PilotParser.get_state()` returns True/False. True = on, False = off + +### Methods + +`getBulbConfig(self)` returns the hardware configuration of the bulb + +`updateState(self)` gets the current bulb state from the light using `sendUDPMessage` and sets it to `self.state` + +`lightSwitch(self)` toggles the light bulb on or off like a switch + +`getMAC(self)` returns the MAC address of the bulb. Can be used as a unique ID + +`sendUDPMessage(self, message, timeout = 60, send_interval = 0.5, max_send_datagrams = 100):` sends the UDP message to the bulb. Since UDP can lose packets, and your light might be a long distance away from the router, we continuously keep sending the UDP command datagram until there is a response from the bulb. In tests this worked way better than just sending once and waiting for a timeout. You can set the async operation timeout using `timeout`, set the time interval to sleep between continuous UDP sends using `send_interval` and the maximum number of continuous pings to send using `max_send_datagrams`. It is already hardcoded to a lower value for `setPilot` (set light state) vs `getPilot` (fetch light state) to avoid flickering the light. + +`turn_off(self)` turns the light off + +`turn_on(PilotBuilder)` turns the light on. This takes a `PilotBuilder` object, which can be used to set all the parameters programmatically - rgb, color temperature, brightness, etc. To set the light to rhythm mode, create an empty `PilotBuilder`. + +## Bulb methods (UDP native): + +- **getSystemConfig** - gets the current system configuration - no parameters required +- **syncPilot** - sent by the bulb as heartbeats +- **getPilot** - gets the current bulb state - no parameters required +- **setPilot** - used to tell the bulb to change color/temp/state +- **Pulse** - uncertain of purpose +- **Registration** - used to "register" with the bulb: This notifies the bulb if you want it to send you heartbeat sync packets + +### Sync functions: + +- syncUserConfig +- syncPilot - {"method":"syncPilot","env":"pro","params":{"mac":"ABCABCABC","rssi":-71,"src":"udp","state":true,"sceneId":0,"temp":6500,"dimming":62}} +- syncSchdPset +- syncBroadcastPilot +- syncSystemConfig +- syncConfig +- syncAlarm + +### Set functions: + +- pulse - {"method":"pulse", "params":{"delta":-15,"duration":300}} +- registration - {"method":"registration","id":105, "params":{"phoneIp":"10.0.0.0","phoneMac":"aaaaaaaaaaaa","register":true}} +- setUserConfig +- setSystemConfig +- setDevInfo +- setSchd +- setSchdPset +- setWifiConfig +- reset +- setFavs +- setState +- setPilot + +### Get functions + +- getPilot +- getUserConfig +- getSystemConfig +- getWifiConfig +- reboot +- getDevInfo + +### Error States and Returns + +- Parse error +- Invalid Request +- Method not found +- Invalid params +- Internal error +- Success + +## Example UDP requests + +Send message to the bulb: +`{"method":"setPilot","params":{"r":255,"g":255,"b":255,"dimming":50}}` +Response: `{"method":"setPilot","env":"pro","result":{"success":true}}` + +Get state of the bulb: +`{"method":"getPilot","params":{}}` +Responses: + +custom color mode: + +`{'method': 'getPilot', 'env': 'pro', 'result': {'mac': 'a8bb50a4f94d', 'rssi': -60, 'src': '', 'state': True, 'sceneId': 0, 'temp': 5075, 'dimming': 47}}` + +scene mode: + +`{'method': 'getPilot', 'env': 'pro', 'result': {'mac': 'a8bb50a4f94d', 'rssi': -65, 'src': '', 'state': True, 'sceneId': 12, 'speed': 100, 'temp': 4200, 'dimming': 47}}` + +rhythm mode: + +`{'method': 'getPilot', 'env': 'pro', 'result': {'mac': 'a8bb50a4f94d', 'rssi': -63, 'src': '', 'state': True, 'sceneId': 14, 'speed': 100, 'dimming': 100, 'schdPsetId': 9}}` + +[code-coverage]: https://codecov.io/gh/sbidy/pywizlight +[code-cover-shield]: https://codecov.io/gh/sbidy/pywizlight/branch/master/graph/badge.svg + + + + +%package -n python3-pywizlight +Summary: A python connector for WiZ light bulbs (e.g SLV Play) +Provides: python-pywizlight +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-pywizlight +![Upload Python Package](https://github.com/sbidy/pywizlight/workflows/Upload%20Python%20Package/badge.svg) + + +[![All Contributors](https://img.shields.io/badge/all_contributors-18-orange.svg?style=flat-square)](#contributors-) + + +![Update Docs](https://github.com/sbidy/pywizlight/workflows/Update%20Docs/badge.svg) +[![Lint](https://github.com/sbidy/pywizlight/actions/workflows/lint.yml/badge.svg)](https://github.com/sbidy/pywizlight/actions/workflows/lint.yml) +[![codecov][code-cover-shield]][code-coverage] + + +# pywizlight + +A Python connector for [WiZ](https://www.wizconnected.com/en/consumer/) devices. + +## Install + +```bash +pip install pywizlight +``` + +**Note:** Requires Python version `>=3.7`. + +### Fedora/CentOS + +On a Fedora-based system or on a CentOS/RHEL 8 machine which has EPEL enabled, as +[`pywizlight`](https://src.fedoraproject.org/rpms/python-pywizlight) is present in the +Fedora Package Collection. + +```bash +sudo dnf -y install python3-pywizlight +``` + +### NixOS + +For NixOS and Nix the latest release of `pywizlight` is usually available in the [`unstable`](https://search.nixos.org/packages?channel=unstable&query=pywizlight) +channel. Stable releases might ship older versions of `pywizlight`. + +```bash +nix-env -iA nixos.python37Packages.pywizlight +``` + +## Contributors + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Seth Nickell

πŸ’»

David Zurow

πŸ“–

Eduardo Ibanez

πŸ“–

Angad Singh

πŸ’»

Fabian Affolter

πŸ“– πŸ’»

Henry Ruhs

πŸ’»

Alberto Panu

πŸ’»

skitterrusty

πŸ’»

Mathias Roth

πŸ’»

durnezj

πŸ’»

NoΓ«lle

πŸ“–

Patrick Kelley

πŸ“–

Ellis Michael

πŸ’»

Gughan Ravikumar

πŸ’»

Charlotte

πŸ’»

Aarni Koskela

πŸ’»

UH-60

πŸ’»

J. Nick Koston

πŸ’»
+ + + + + + +## Discover bulbs via CLI + +To find bulbs via cli you can use the following: +```bash +python -m pywizlight.cli discover +``` + +## Example + +```python +import asyncio + +from pywizlight import wizlight, PilotBuilder, discovery + +async def main(): + """Sample code to work with bulbs.""" + # Discover all bulbs in the network via broadcast datagram (UDP) + # function takes the discovery object and returns a list of wizlight objects. + bulbs = await discovery.discover_lights(broadcast_space="192.168.1.255") + # Print the IP address of the bulb on index 0 + print(f"Bulb IP address: {bulbs[0].ip}") + + # Iterate over all returned bulbs + for bulb in bulbs: + print(bulb.__dict__) + # Turn off all available bulbs + # await bulb.turn_off() + + # Set up a standard light + light = wizlight("192.168.1.27") + # Set up the light with a custom port + #light = wizlight("your bulb's IP address", port=12345) + + # The following calls need to be done inside an asyncio coroutine + # to run them from normal synchronous code, you can wrap them with + # asyncio.run(..). + + # Turn the light on into "rhythm mode" + await light.turn_on(PilotBuilder()) + # Set bulb brightness + await light.turn_on(PilotBuilder(brightness = 255)) + + # Set bulb brightness (with async timeout) + timeout = 10 + await asyncio.wait_for(light.turn_on(PilotBuilder(brightness = 255)), timeout) + + # Set bulb to warm white + await light.turn_on(PilotBuilder(warm_white = 255)) + + # Set RGB values + # red to 0 = 0%, green to 128 = 50%, blue to 255 = 100% + await light.turn_on(PilotBuilder(rgb = (0, 128, 255))) + + # Get the current color temperature, RGB values + state = await light.updateState() + print(state.get_colortemp()) + red, green, blue = state.get_rgb() + print(f"red {red}, green {green}, blue {blue}") + + # Start a scene + await light.turn_on(PilotBuilder(scene = 4)) # party + + # Get the name of the current scene + state = await light.updateState() + print(state.get_scene()) + + # Get the features of the bulb + bulb_type = await bulbs[0].get_bulbtype() + print(bulb_type.features.brightness) # returns True if brightness is supported + print(bulb_type.features.color) # returns True if color is supported + print(bulb_type.features.color_tmp) # returns True if color temperatures are supported + print(bulb_type.features.effect) # returns True if effects are supported + print(bulb_type.kelvin_range.max) # returns max kelvin in INT + print(bulb_type.kelvin_range.min) # returns min kelvin in INT + print(bulb_type.name) # returns the module name of the bulb + + # Turn the light off + await light.turn_off() + + # Do operations on multiple lights in parallel + #bulb1 = wizlight("") + #bulb2 = wizlight("") + # --- DEPRECATED in 3.10 see [#140](https://github.com/sbidy/pywizlight/issues/140) + # await asyncio.gather(bulb1.turn_on(PilotBuilder(brightness = 255)), + # bulb2.turn_on(PilotBuilder(warm_white = 255))) + # --- For >3.10 await asyncio.gather() from another coroutine + # async def turn_bulbs_on(bulb1, bulb2): + # await asyncio.gather(bulb1.turn_on(PilotBuilder(warm_white=255)), bulb2.turn_on(PilotBuilder(warm_white=255))) + # def main: + # asyncio.run(async turn_bulbs_on(bulb1, bulb2)) + +loop = asyncio.get_event_loop() +loop.run_until_complete(main()) +``` + +## CLI + +`wizlight` is a command-line tool to perform basic interactions with bulbs. + +```console +$ wizlight +Usage: wizlight [OPTIONS] COMMAND [ARGS]... + + Simple command-line tool to interact with Wizlight bulbs. + +Options: + --version Show the version and exit. + --help Show this message and exit. + +Commands: + discover Discover bulb in the local network. + off Turn the bulb off. + on Turn the bulb on. + state Get the current state from the given bulb. +``` + +## Discovery + +The discovery works with a UDP Broadcast request and collects all bulbs in the network. + +## Bulb paramters (UDP RAW) + +- **sceneId** - calls one of the predefined scenes (int from 1 to 32) [List of names in code](https://github.com/sbidy/pywizlight/blob/master/pywizlight/scenes.py) +- **speed** - sets the color changing speed in percent +- **dimming** - sets the dimmer of the bulb in percent +- **temp** - sets the color temperature in kelvins +- **r** - red color range 0-255 +- **g** - green color range 0-255 +- **b** - blue color range 0-255 +- **c** - cold white range 0-255 +- **w** - warm white range 0-255 +- **id** - the bulb id +- **state** - whether it's on or off +- **schdPsetId** - rhythm id of the room + +## Async I/O + +For async I/O this component uses Python's built-in asyncio DatagramTransport, which allows completely non-blocking UDP transport. + +## Classes + +`wizlight(ip)`: Creates an instance of a WiZ Light Bulb. Constructed with the IP addCancel changesress of the bulb. + +### Instance variables + +First you need to fetch the state by calling `light.updateState()`. +After that all states can be fetched from `light.state`, which is a `PilotParser` object. + +`PilotParser.get_brightness()`gets the value of the brightness 0-255 + +`PilotParser.get_rgb()` gets the rgbW color state of the bulb + +`PilotParser.get_colortemp()` gets the color temperature of the bulb + +`PilotParser.get_warm_white/get_cold_white()` gets the current warm/cold setting (not supported by original Philips Wiz bulbs) + +`PilotParser.get_scene()` gets the current scene name + +`PilotParser.get_state()` returns True/False. True = on, False = off + +### Methods + +`getBulbConfig(self)` returns the hardware configuration of the bulb + +`updateState(self)` gets the current bulb state from the light using `sendUDPMessage` and sets it to `self.state` + +`lightSwitch(self)` toggles the light bulb on or off like a switch + +`getMAC(self)` returns the MAC address of the bulb. Can be used as a unique ID + +`sendUDPMessage(self, message, timeout = 60, send_interval = 0.5, max_send_datagrams = 100):` sends the UDP message to the bulb. Since UDP can lose packets, and your light might be a long distance away from the router, we continuously keep sending the UDP command datagram until there is a response from the bulb. In tests this worked way better than just sending once and waiting for a timeout. You can set the async operation timeout using `timeout`, set the time interval to sleep between continuous UDP sends using `send_interval` and the maximum number of continuous pings to send using `max_send_datagrams`. It is already hardcoded to a lower value for `setPilot` (set light state) vs `getPilot` (fetch light state) to avoid flickering the light. + +`turn_off(self)` turns the light off + +`turn_on(PilotBuilder)` turns the light on. This takes a `PilotBuilder` object, which can be used to set all the parameters programmatically - rgb, color temperature, brightness, etc. To set the light to rhythm mode, create an empty `PilotBuilder`. + +## Bulb methods (UDP native): + +- **getSystemConfig** - gets the current system configuration - no parameters required +- **syncPilot** - sent by the bulb as heartbeats +- **getPilot** - gets the current bulb state - no parameters required +- **setPilot** - used to tell the bulb to change color/temp/state +- **Pulse** - uncertain of purpose +- **Registration** - used to "register" with the bulb: This notifies the bulb if you want it to send you heartbeat sync packets + +### Sync functions: + +- syncUserConfig +- syncPilot - {"method":"syncPilot","env":"pro","params":{"mac":"ABCABCABC","rssi":-71,"src":"udp","state":true,"sceneId":0,"temp":6500,"dimming":62}} +- syncSchdPset +- syncBroadcastPilot +- syncSystemConfig +- syncConfig +- syncAlarm + +### Set functions: + +- pulse - {"method":"pulse", "params":{"delta":-15,"duration":300}} +- registration - {"method":"registration","id":105, "params":{"phoneIp":"10.0.0.0","phoneMac":"aaaaaaaaaaaa","register":true}} +- setUserConfig +- setSystemConfig +- setDevInfo +- setSchd +- setSchdPset +- setWifiConfig +- reset +- setFavs +- setState +- setPilot + +### Get functions + +- getPilot +- getUserConfig +- getSystemConfig +- getWifiConfig +- reboot +- getDevInfo + +### Error States and Returns + +- Parse error +- Invalid Request +- Method not found +- Invalid params +- Internal error +- Success + +## Example UDP requests + +Send message to the bulb: +`{"method":"setPilot","params":{"r":255,"g":255,"b":255,"dimming":50}}` +Response: `{"method":"setPilot","env":"pro","result":{"success":true}}` + +Get state of the bulb: +`{"method":"getPilot","params":{}}` +Responses: + +custom color mode: + +`{'method': 'getPilot', 'env': 'pro', 'result': {'mac': 'a8bb50a4f94d', 'rssi': -60, 'src': '', 'state': True, 'sceneId': 0, 'temp': 5075, 'dimming': 47}}` + +scene mode: + +`{'method': 'getPilot', 'env': 'pro', 'result': {'mac': 'a8bb50a4f94d', 'rssi': -65, 'src': '', 'state': True, 'sceneId': 12, 'speed': 100, 'temp': 4200, 'dimming': 47}}` + +rhythm mode: + +`{'method': 'getPilot', 'env': 'pro', 'result': {'mac': 'a8bb50a4f94d', 'rssi': -63, 'src': '', 'state': True, 'sceneId': 14, 'speed': 100, 'dimming': 100, 'schdPsetId': 9}}` + +[code-coverage]: https://codecov.io/gh/sbidy/pywizlight +[code-cover-shield]: https://codecov.io/gh/sbidy/pywizlight/branch/master/graph/badge.svg + + + + +%package help +Summary: Development documents and examples for pywizlight +Provides: python3-pywizlight-doc +%description help +![Upload Python Package](https://github.com/sbidy/pywizlight/workflows/Upload%20Python%20Package/badge.svg) + + +[![All Contributors](https://img.shields.io/badge/all_contributors-18-orange.svg?style=flat-square)](#contributors-) + + +![Update Docs](https://github.com/sbidy/pywizlight/workflows/Update%20Docs/badge.svg) +[![Lint](https://github.com/sbidy/pywizlight/actions/workflows/lint.yml/badge.svg)](https://github.com/sbidy/pywizlight/actions/workflows/lint.yml) +[![codecov][code-cover-shield]][code-coverage] + + +# pywizlight + +A Python connector for [WiZ](https://www.wizconnected.com/en/consumer/) devices. + +## Install + +```bash +pip install pywizlight +``` + +**Note:** Requires Python version `>=3.7`. + +### Fedora/CentOS + +On a Fedora-based system or on a CentOS/RHEL 8 machine which has EPEL enabled, as +[`pywizlight`](https://src.fedoraproject.org/rpms/python-pywizlight) is present in the +Fedora Package Collection. + +```bash +sudo dnf -y install python3-pywizlight +``` + +### NixOS + +For NixOS and Nix the latest release of `pywizlight` is usually available in the [`unstable`](https://search.nixos.org/packages?channel=unstable&query=pywizlight) +channel. Stable releases might ship older versions of `pywizlight`. + +```bash +nix-env -iA nixos.python37Packages.pywizlight +``` + +## Contributors + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Seth Nickell

πŸ’»

David Zurow

πŸ“–

Eduardo Ibanez

πŸ“–

Angad Singh

πŸ’»

Fabian Affolter

πŸ“– πŸ’»

Henry Ruhs

πŸ’»

Alberto Panu

πŸ’»

skitterrusty

πŸ’»

Mathias Roth

πŸ’»

durnezj

πŸ’»

NoΓ«lle

πŸ“–

Patrick Kelley

πŸ“–

Ellis Michael

πŸ’»

Gughan Ravikumar

πŸ’»

Charlotte

πŸ’»

Aarni Koskela

πŸ’»

UH-60

πŸ’»

J. Nick Koston

πŸ’»
+ + + + + + +## Discover bulbs via CLI + +To find bulbs via cli you can use the following: +```bash +python -m pywizlight.cli discover +``` + +## Example + +```python +import asyncio + +from pywizlight import wizlight, PilotBuilder, discovery + +async def main(): + """Sample code to work with bulbs.""" + # Discover all bulbs in the network via broadcast datagram (UDP) + # function takes the discovery object and returns a list of wizlight objects. + bulbs = await discovery.discover_lights(broadcast_space="192.168.1.255") + # Print the IP address of the bulb on index 0 + print(f"Bulb IP address: {bulbs[0].ip}") + + # Iterate over all returned bulbs + for bulb in bulbs: + print(bulb.__dict__) + # Turn off all available bulbs + # await bulb.turn_off() + + # Set up a standard light + light = wizlight("192.168.1.27") + # Set up the light with a custom port + #light = wizlight("your bulb's IP address", port=12345) + + # The following calls need to be done inside an asyncio coroutine + # to run them from normal synchronous code, you can wrap them with + # asyncio.run(..). + + # Turn the light on into "rhythm mode" + await light.turn_on(PilotBuilder()) + # Set bulb brightness + await light.turn_on(PilotBuilder(brightness = 255)) + + # Set bulb brightness (with async timeout) + timeout = 10 + await asyncio.wait_for(light.turn_on(PilotBuilder(brightness = 255)), timeout) + + # Set bulb to warm white + await light.turn_on(PilotBuilder(warm_white = 255)) + + # Set RGB values + # red to 0 = 0%, green to 128 = 50%, blue to 255 = 100% + await light.turn_on(PilotBuilder(rgb = (0, 128, 255))) + + # Get the current color temperature, RGB values + state = await light.updateState() + print(state.get_colortemp()) + red, green, blue = state.get_rgb() + print(f"red {red}, green {green}, blue {blue}") + + # Start a scene + await light.turn_on(PilotBuilder(scene = 4)) # party + + # Get the name of the current scene + state = await light.updateState() + print(state.get_scene()) + + # Get the features of the bulb + bulb_type = await bulbs[0].get_bulbtype() + print(bulb_type.features.brightness) # returns True if brightness is supported + print(bulb_type.features.color) # returns True if color is supported + print(bulb_type.features.color_tmp) # returns True if color temperatures are supported + print(bulb_type.features.effect) # returns True if effects are supported + print(bulb_type.kelvin_range.max) # returns max kelvin in INT + print(bulb_type.kelvin_range.min) # returns min kelvin in INT + print(bulb_type.name) # returns the module name of the bulb + + # Turn the light off + await light.turn_off() + + # Do operations on multiple lights in parallel + #bulb1 = wizlight("") + #bulb2 = wizlight("") + # --- DEPRECATED in 3.10 see [#140](https://github.com/sbidy/pywizlight/issues/140) + # await asyncio.gather(bulb1.turn_on(PilotBuilder(brightness = 255)), + # bulb2.turn_on(PilotBuilder(warm_white = 255))) + # --- For >3.10 await asyncio.gather() from another coroutine + # async def turn_bulbs_on(bulb1, bulb2): + # await asyncio.gather(bulb1.turn_on(PilotBuilder(warm_white=255)), bulb2.turn_on(PilotBuilder(warm_white=255))) + # def main: + # asyncio.run(async turn_bulbs_on(bulb1, bulb2)) + +loop = asyncio.get_event_loop() +loop.run_until_complete(main()) +``` + +## CLI + +`wizlight` is a command-line tool to perform basic interactions with bulbs. + +```console +$ wizlight +Usage: wizlight [OPTIONS] COMMAND [ARGS]... + + Simple command-line tool to interact with Wizlight bulbs. + +Options: + --version Show the version and exit. + --help Show this message and exit. + +Commands: + discover Discover bulb in the local network. + off Turn the bulb off. + on Turn the bulb on. + state Get the current state from the given bulb. +``` + +## Discovery + +The discovery works with a UDP Broadcast request and collects all bulbs in the network. + +## Bulb paramters (UDP RAW) + +- **sceneId** - calls one of the predefined scenes (int from 1 to 32) [List of names in code](https://github.com/sbidy/pywizlight/blob/master/pywizlight/scenes.py) +- **speed** - sets the color changing speed in percent +- **dimming** - sets the dimmer of the bulb in percent +- **temp** - sets the color temperature in kelvins +- **r** - red color range 0-255 +- **g** - green color range 0-255 +- **b** - blue color range 0-255 +- **c** - cold white range 0-255 +- **w** - warm white range 0-255 +- **id** - the bulb id +- **state** - whether it's on or off +- **schdPsetId** - rhythm id of the room + +## Async I/O + +For async I/O this component uses Python's built-in asyncio DatagramTransport, which allows completely non-blocking UDP transport. + +## Classes + +`wizlight(ip)`: Creates an instance of a WiZ Light Bulb. Constructed with the IP addCancel changesress of the bulb. + +### Instance variables + +First you need to fetch the state by calling `light.updateState()`. +After that all states can be fetched from `light.state`, which is a `PilotParser` object. + +`PilotParser.get_brightness()`gets the value of the brightness 0-255 + +`PilotParser.get_rgb()` gets the rgbW color state of the bulb + +`PilotParser.get_colortemp()` gets the color temperature of the bulb + +`PilotParser.get_warm_white/get_cold_white()` gets the current warm/cold setting (not supported by original Philips Wiz bulbs) + +`PilotParser.get_scene()` gets the current scene name + +`PilotParser.get_state()` returns True/False. True = on, False = off + +### Methods + +`getBulbConfig(self)` returns the hardware configuration of the bulb + +`updateState(self)` gets the current bulb state from the light using `sendUDPMessage` and sets it to `self.state` + +`lightSwitch(self)` toggles the light bulb on or off like a switch + +`getMAC(self)` returns the MAC address of the bulb. Can be used as a unique ID + +`sendUDPMessage(self, message, timeout = 60, send_interval = 0.5, max_send_datagrams = 100):` sends the UDP message to the bulb. Since UDP can lose packets, and your light might be a long distance away from the router, we continuously keep sending the UDP command datagram until there is a response from the bulb. In tests this worked way better than just sending once and waiting for a timeout. You can set the async operation timeout using `timeout`, set the time interval to sleep between continuous UDP sends using `send_interval` and the maximum number of continuous pings to send using `max_send_datagrams`. It is already hardcoded to a lower value for `setPilot` (set light state) vs `getPilot` (fetch light state) to avoid flickering the light. + +`turn_off(self)` turns the light off + +`turn_on(PilotBuilder)` turns the light on. This takes a `PilotBuilder` object, which can be used to set all the parameters programmatically - rgb, color temperature, brightness, etc. To set the light to rhythm mode, create an empty `PilotBuilder`. + +## Bulb methods (UDP native): + +- **getSystemConfig** - gets the current system configuration - no parameters required +- **syncPilot** - sent by the bulb as heartbeats +- **getPilot** - gets the current bulb state - no parameters required +- **setPilot** - used to tell the bulb to change color/temp/state +- **Pulse** - uncertain of purpose +- **Registration** - used to "register" with the bulb: This notifies the bulb if you want it to send you heartbeat sync packets + +### Sync functions: + +- syncUserConfig +- syncPilot - {"method":"syncPilot","env":"pro","params":{"mac":"ABCABCABC","rssi":-71,"src":"udp","state":true,"sceneId":0,"temp":6500,"dimming":62}} +- syncSchdPset +- syncBroadcastPilot +- syncSystemConfig +- syncConfig +- syncAlarm + +### Set functions: + +- pulse - {"method":"pulse", "params":{"delta":-15,"duration":300}} +- registration - {"method":"registration","id":105, "params":{"phoneIp":"10.0.0.0","phoneMac":"aaaaaaaaaaaa","register":true}} +- setUserConfig +- setSystemConfig +- setDevInfo +- setSchd +- setSchdPset +- setWifiConfig +- reset +- setFavs +- setState +- setPilot + +### Get functions + +- getPilot +- getUserConfig +- getSystemConfig +- getWifiConfig +- reboot +- getDevInfo + +### Error States and Returns + +- Parse error +- Invalid Request +- Method not found +- Invalid params +- Internal error +- Success + +## Example UDP requests + +Send message to the bulb: +`{"method":"setPilot","params":{"r":255,"g":255,"b":255,"dimming":50}}` +Response: `{"method":"setPilot","env":"pro","result":{"success":true}}` + +Get state of the bulb: +`{"method":"getPilot","params":{}}` +Responses: + +custom color mode: + +`{'method': 'getPilot', 'env': 'pro', 'result': {'mac': 'a8bb50a4f94d', 'rssi': -60, 'src': '', 'state': True, 'sceneId': 0, 'temp': 5075, 'dimming': 47}}` + +scene mode: + +`{'method': 'getPilot', 'env': 'pro', 'result': {'mac': 'a8bb50a4f94d', 'rssi': -65, 'src': '', 'state': True, 'sceneId': 12, 'speed': 100, 'temp': 4200, 'dimming': 47}}` + +rhythm mode: + +`{'method': 'getPilot', 'env': 'pro', 'result': {'mac': 'a8bb50a4f94d', 'rssi': -63, 'src': '', 'state': True, 'sceneId': 14, 'speed': 100, 'dimming': 100, 'schdPsetId': 9}}` + +[code-coverage]: https://codecov.io/gh/sbidy/pywizlight +[code-cover-shield]: https://codecov.io/gh/sbidy/pywizlight/branch/master/graph/badge.svg + + + + +%prep +%autosetup -n pywizlight-0.5.14 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-pywizlight -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Thu Mar 09 2023 Python_Bot - 0.5.14-1 +- Package Spec generated diff --git a/sources b/sources new file mode 100644 index 0000000..07176e5 --- /dev/null +++ b/sources @@ -0,0 +1 @@ +e9a773920864a91b4369c96dee167aa6 pywizlight-0.5.14.tar.gz -- cgit v1.2.3