In the first part we gain the ability to control the power switches in Home Assistant.
Now we can utilize the API to control the status of the power switch. For now i use the homeassistant_api in python, to do this. Below you can find an example.
Looking ahead, the vision includes functionalities for power management, accommodating various setups, preserving lab states, and simplifying state recovery. From my standpoint, it’s crucial to have the capability to schedule the lab’s operation based on specific timeframes or power it down when it’s idle. This scheduling feature proves especially beneficial when I’m away or in need of rest.
As time permits, I plan to reconfigure my setup to incorporate NetBox for documenting my homelab’s details, such as cabling, power outlets, and console or KVM connections.
Features like HP / Aruba Zero Touch Provisioning provides a way to configure the lab to a certain state or can easily be tested.
Example:
#!/usr/bin/env python
#
# Copyright 2023 Patrick Marc Preuss
#
import os
import sys
import yaml
import argparse
from homeassistant_api import Client
import logging
def get_config(filename):
"""Read the config file and return this."""
fn = os.path.join(os.path.expanduser("~"), filename)
config = None
try:
with open(fn, "r") as stream:
try:
config = yaml.safe_load(stream)
except yaml.YAMLError as e:
print(e)
return None
except FileNotFoundError as e:
print(e)
return None
except IOError:
print("io error")
pass
return config
def get_entity_id(config, device_name):
"""Get the entity_id for a device name form the config."""
entity_ids = [
d.get("entity_id")
for d in config["devices"]
if d.get("entity_id") and d.get("name") == device_name
]
if not len(entity_ids) == 1:
return None
entity_id = entity_ids[0]
return entity_id
def change_status(api_url, token, device_name, device_entity_id, state):
"""Change the status of en device (entity_id) and print the name."""
with Client(
api_url,
token,
) as client:
switches = client.get_domain("switch")
if state == "on":
switches.turn_on(entity_id=device_entity_id)
elif state == "off":
switches.turn_off(entity_id=device_entity_id)
elif state == "toggle":
switches.toggle(entity_id=device_entity_id)
else:
return None
return state
def check_status(api_url, token, device_name, device_entity_id):
"""Check the status in homeassistant."""
with Client(
api_url,
token,
) as client:
device = client.get_entity(entity_id=device_entity_id)
state = device.get_state()
return state.state
def main(argv):
"""Do the Main function."""
logging.basicConfig(format="%(asctime)s %(message)s", level=logging.INFO)
config = get_config(".power-config.yml")
token = config["token"]
api_url = config["api_url"]
parser = argparse.ArgumentParser()
parser.add_argument(
"--device", help="The Device to change/check state.", required=True
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
"--state",
choices=["on", "off", "toggle"],
help="new state on/off/toggle",
)
group.add_argument("--check", action="store_true", help="check the state")
args = parser.parse_args()
assert token is not None
if not args.device:
raise Exception("--device is required")
device_name = args.device
entity_id = get_entity_id(config, device_name)
if (not args.state and not args.check) or args.check:
# check_status(api_url, token, args.device)
state = check_status(api_url, token, device_name, entity_id)
logging.info("%s is %s", device_name, state)
return 0
state = change_status(api_url, token, device_name, entity_id, args.state)
logging.info("%s is now %s", device_name, state)
if __name__ == "__main__":
main(sys.argv[1:])
The config ~/.power-config.yml
api_url: http://:8123/api
token:
devices:
- name: coffee
entity_id: switch.coffee_switch
License
Copyright 2023 Patrick Marc Preuss All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Photo by Thomas Kelley on Unsplash

