Skip to content

Commit

Permalink
Version dum tp: 0.1
Browse files Browse the repository at this point in the history
moved shell into package and added setup shim
moved shell into main() to utilize setup.py console_script handler
  • Loading branch information
joscha82 committed Apr 20, 2022
1 parent ae3e37c commit ca98bb3
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 89 deletions.
1 change: 1 addition & 0 deletions Manifest.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include wattpilot.yaml
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Run the interactive shell

```bash
# Usage:
python3 shell.py <wattpilot_ip> <password>
wattpilotshell <wattpilot_ip> <password>
> help
Wattpilot Shell Commands:
dump: Dump all property values
Expand All @@ -38,11 +38,11 @@ It's also possible to pass a single command to the shell to integrate it into sc
```bash
# Usage:
python3 shell.py <wattpilot_ip> <password> "<command> <args...>"
wattpilotshell <wattpilot_ip> <password> "<command> <args...>"
# Examples:
python3 shell.py <wattpilot_ip> <password> "get amp"
python3 shell.py <wattpilot_ip> <password> "set amp 6"
wattpilotshell <wattpilot_ip> <password> "get amp"
wattpilotshell <wattpilot_ip> <password> "set amp 6"
```
## Environment Variables
Expand Down Expand Up @@ -77,5 +77,5 @@ export MQTT_ENABLED=true
export MQTT_HOST=<mqtt_host>
export WATTPILOT_HOST=<wattpilot_ip>
export WATTPILOT_PASSWORD=<wattpilot_password>
python3 shell.py
wattpilotshell
```
6 changes: 5 additions & 1 deletion changelog.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
Version 0.0.1 (2022-02-17)
- Initial testing release
- Initial testing release

Version 0.1 (2022-04-20)
- New Featuer: shell (@ahochsteger)
- New Feature: mqtt Bridge in shell (@ahochsteger)
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ requires = [
"pyyaml>=5.4.1",
"setuptools>=42",
"wheel",
"pyreadline"
"pyreadline",
"paho-mqtt"
]
build-backend = "setuptools.build_meta"
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
websocket-client>=1.2.3
PyYAML>=5.4.1
pyreadline
paho-mqtt
11 changes: 8 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

setup(
name='wattpilot',
version='0.0.1',
version='0.1',
description='Python library to connect to a Fronius Wattpilot Wallbox',
long_description=long_description,
long_description_content_type='text/markdown',
Expand All @@ -31,12 +31,17 @@
keywords='wattpilot',
package_dir={'': 'src'},
packages=find_packages(where='src'),
entry_points = {
'console_scripts': ['wattpilotshell=wattpilot.wattpilotshell:main'],
},
package_data = { '' : ['wattpilot.yaml'] },
python_requires='>=3.9, <4',
install_requires=['websocket-client','PyYAML','pyreadline'],
install_requires=['websocket-client','PyYAML','pyreadline','paho-mqtt'],
platforms="any",
license="MIT License",
project_urls={
'Bug Reports': 'https://github.com/joscha82/wattpilot/issues',
'Source': 'https://github.com/joscha82/wattpilot'
}
},
include_package_data=True
)
Empty file.
File renamed without changes.
205 changes: 126 additions & 79 deletions shell.py → src/wattpilot/wattpilotshell.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import argparse
from ast import arg
import json
import logging
import os
import paho.mqtt.client as mqtt
import readline
import wattpilot
import yaml
import pkgutil

from time import sleep
from types import SimpleNamespace

_LOGGER = logging.getLogger(__name__)

def print_prop_info(wpi, propName, value):
global wpi_properties

#propInfo = next((x for x in wpi['properties'] if x['key'] == propName), None)
propInfo = wpi_properties[propName]
_LOGGER.debug(f"Property info: {propInfo}")
Expand Down Expand Up @@ -42,6 +46,7 @@ def print_prop_info(wpi, propName, value):
print(f" Example: {propInfo['example']}")

def watch_properties(name,value):
global watch_properties
if name in watching_properties:
_LOGGER.info(f"Property {name} changed to {value}")

Expand All @@ -58,6 +63,7 @@ def cmd_get(wp, args):
print(wp.allProps[args[0]])

def cmd_mqtt_connect(wp, args):
global mqtt_client
if len(args) != 2:
_LOGGER.error(f"Wrong number of arguments: mqtt-connect <host> <port>")
else:
Expand Down Expand Up @@ -87,12 +93,15 @@ def cmd_set(wp, args):
wp.send_update(args[0],v)

def cmd_watch_message(wp, name):
global watch_messages

if len(watching_messages) == 0:
wp.register_message_callback(watch_messages)
if name not in watching_messages:
watching_messages.append(name)

def cmd_watch_property(wp, name):
global watch_properties
if len(watching_properties) == 0:
wp.register_property_callback(watch_properties)
if name not in watching_properties:
Expand Down Expand Up @@ -131,6 +140,11 @@ def cmd_unwatch(wp,args):
_LOGGER.error(f"Unknown watch type: {args[0]}")

def process_command(wp, wpi, cmdline):

global cmd_parser
global parse
global wpi_properties

"""Process a Wattpilot shell command"""
exit = False
cmd_args = cmd_parser.parse_args(args=cmdline.strip().split(' '))
Expand Down Expand Up @@ -205,6 +219,13 @@ def wait_timeout(fn, timeout):
# MQTT functions

def mqtt_message(wp,wsapp,msg,msg_json):
global MQTT_PUBLISH_MESSAGES
global MQTT_BASE_TOPIC
global MQTT_PUBLISH_PROPERTIES
global MQTT_WATCH_PROPERTIES
global MQTT_PROPERTY_READ_TOPIC_PATTERN
global MQTT_MESSAGE_TOPIC_PATTERN

if mqtt_client == None:
_LOGGER.debug(f"Skipping MQTT message publishing.")
return
Expand All @@ -228,56 +249,78 @@ def mqtt_message(wp,wsapp,msg,msg_json):
.replace("{propName}",prop_name)
mqtt_client.publish(property_topic, json.dumps(value))

# Timeout config:
WATTPILOT_CONNECT_TIMEOUT = int(os.environ.get('WATTPILOT_CONNECT_TIMEOUT','30'))
WATTPILOT_INITIALIZED_TIMEOUT = int(os.environ.get('WATTPILOT_INITIALIZED_TIMEOUT','30'))

# Globals:
watching_properties = []
watching_messages = []
mqtt_client = None

# MQTT config:
MQTT_ENABLED = os.environ.get('MQTT_ENABLED','false')
if MQTT_ENABLED == "true":
MQTT_CLIENT_ID = os.environ.get('MQTT_CLIENT_ID','wattpilot2mqtt')
MQTT_HA_DISCOVERY_ENABLED = os.environ.get('MQTT_HA_DISCOVERY_ENABLED','false')
MQTT_HOST = os.environ.get('MQTT_HOST')
MQTT_PORT = int(os.environ.get('MQTT_PORT','1883'))
MQTT_BASE_TOPIC = os.environ.get('MQTT_BASE_TOPIC','wattpilot')
MQTT_PUBLISH_MESSAGES = os.environ.get('MQTT_PUBLISH_MESSAGES','true')
MQTT_MESSAGE_TOPIC_PATTERN = os.environ.get('MQTT_MESSAGE_TOPIC_PATTERN','{baseTopic}/{serialNumber}/messages/{messageType}')
MQTT_PUBLISH_PROPERTIES = os.environ.get('MQTT_PUBLISH_PROPERTIES','false')
MQTT_WATCH_PROPERTIES = os.environ.get('MQTT_WATCH_PROPERTIES','amp car fna lmo sse').split(sep=' ')
MQTT_PROPERTY_READ_TOPIC_PATTERN = os.environ.get('MQTT_PROPERTY_READ_TOPIC_PATTERN','{baseTopic}/{serialNumber}/properties/{propName}')
MQTT_PROPERTY_SET_TOPIC_PATTERN = os.environ.get('MQTT_PROPERTY_SET_TOPIC_PATTERN','{baseTopic}/{serialNumber}/properties/{propName}/set')
mqtt_client = mqtt.Client(MQTT_CLIENT_ID)
mqtt_client.connect(MQTT_HOST, MQTT_PORT)


# Set debug level:
logging.basicConfig(level=os.environ.get('WATTPILOT_DEBUG_LEVEL','INFO'))

# Setup readline
readline.parse_and_bind("tab: complete")
readline.set_completer(complete)

# Commandline argument parser:
parser = argparse.ArgumentParser()
parser.add_argument("ip", help = "IP of Wattpilot Device", nargs="?")
parser.add_argument("password", help = "Password of Wattpilot", nargs="?")
parser.add_argument("cmdline", help = "Optional shell command", nargs="?")
args = parser.parse_args()

# Wattpilot shell command parser:
cmd_parser = argparse.ArgumentParser()
cmd_parser.add_argument("cmd", help = "Command")
cmd_parser.add_argument("args", help = "Arguments", nargs='*')

# Read Wattpilot config:
with open("wattpilot.yaml", 'r') as stream:

def main():
# Timeout config:
WATTPILOT_CONNECT_TIMEOUT = int(os.environ.get('WATTPILOT_CONNECT_TIMEOUT','30'))
WATTPILOT_INITIALIZED_TIMEOUT = int(os.environ.get('WATTPILOT_INITIALIZED_TIMEOUT','30'))

# Globals:
global cmd_parser
global parser
global wpi_properties
global wpi_messages
global mqtt_client
global watching_messages
global watching_properties
global MQTT_PUBLISH_MESSAGES
global MQTT_BASE_TOPIC
global MQTT_PUBLISH_PROPERTIES
global MQTT_WATCH_PROPERTIES
global MQTT_PROPERTY_READ_TOPIC_PATTERN
global MQTT_MESSAGE_TOPIC_PATTERN


watching_properties = []
watching_messages = []
mqtt_client = None

# MQTT config:
MQTT_ENABLED = os.environ.get('MQTT_ENABLED','false')
if MQTT_ENABLED == "true":
MQTT_CLIENT_ID = os.environ.get('MQTT_CLIENT_ID','wattpilot2mqtt')
MQTT_HA_DISCOVERY_ENABLED = os.environ.get('MQTT_HA_DISCOVERY_ENABLED','false')
MQTT_HOST = os.environ.get('MQTT_HOST')
MQTT_PORT = int(os.environ.get('MQTT_PORT','1883'))
MQTT_BASE_TOPIC = os.environ.get('MQTT_BASE_TOPIC','wattpilot')
MQTT_PUBLISH_MESSAGES = os.environ.get('MQTT_PUBLISH_MESSAGES','true')
MQTT_MESSAGE_TOPIC_PATTERN = os.environ.get('MQTT_MESSAGE_TOPIC_PATTERN','{baseTopic}/{serialNumber}/messages/{messageType}')
MQTT_PUBLISH_PROPERTIES = os.environ.get('MQTT_PUBLISH_PROPERTIES','false')
MQTT_WATCH_PROPERTIES = os.environ.get('MQTT_WATCH_PROPERTIES','amp car fna lmo sse').split(sep=' ')
MQTT_PROPERTY_READ_TOPIC_PATTERN = os.environ.get('MQTT_PROPERTY_READ_TOPIC_PATTERN','{baseTopic}/{serialNumber}/properties/{propName}')
MQTT_PROPERTY_SET_TOPIC_PATTERN = os.environ.get('MQTT_PROPERTY_SET_TOPIC_PATTERN','{baseTopic}/{serialNumber}/properties/{propName}/set')
mqtt_client = mqtt.Client(MQTT_CLIENT_ID)
mqtt_client.connect(MQTT_HOST, MQTT_PORT)


# Set debug level:
logging.basicConfig(level=os.environ.get('WATTPILOT_DEBUG_LEVEL','INFO'))

# Setup readline
readline.parse_and_bind("tab: complete")
readline.set_completer(complete)

# Commandline argument parser:
parser = argparse.ArgumentParser()
parser.add_argument("ip", help = "IP of Wattpilot Device", nargs="?")
parser.add_argument("password", help = "Password of Wattpilot", nargs="?")
parser.add_argument("cmdline", help = "Optional shell command", nargs="?")

args = parser.parse_args()

# Wattpilot shell command parser:
cmd_parser = argparse.ArgumentParser()
cmd_parser.add_argument("cmd", help = "Command")
cmd_parser.add_argument("args", help = "Arguments", nargs='*')

# Read Wattpilot config:

wattpilotAPIDesc = pkgutil.get_data(__name__, "ressources/wattpilot.yaml")
#wattpilotAPIDesc = "wattpilot.yaml"


try:
wpi=yaml.safe_load(stream)
wpi=yaml.safe_load(wattpilotAPIDesc)
wpi_messages = dict(zip(
[x["key"] for x in wpi["messages"]],
[x for x in wpi["messages"]],
Expand All @@ -289,33 +332,37 @@ def mqtt_message(wp,wsapp,msg,msg_json):
except yaml.YAMLError as exc:
print(exc)

# Connect to Wattpilot:
ip = args.ip or os.environ.get('WATTPILOT_HOST')
password = args.password or os.environ.get('WATTPILOT_PASSWORD')
wp = wattpilot.Wattpilot(ip,password)

# Enable MQTT integration:
if MQTT_ENABLED == "true":
_LOGGER.debug(f"Registering message callback for MQTT integration.")
wp.register_message_callback(mqtt_message)
wp.connect()

# Wait for connection and initializon:
wait_timeout(lambda: wp.connected, WATTPILOT_CONNECT_TIMEOUT) or exit("ERROR: Timeout while connecting to Wattpilot!")
wait_timeout(lambda: wp.allPropsInitialized, WATTPILOT_INITIALIZED_TIMEOUT) or exit("ERROR: Timeout while waiting for property initialization!")

# Process commands:
if args.cmdline:
# Process commands passed on the commandline
process_command(wp,wpi,args.cmdline)
else:
# Start interactive shell
process_command(wp,wpi,"info")
exit=False
while not exit:
try:
command = input('> ')
except EOFError as e:
command = "exit"
print()
exit = process_command(wp,wpi,command)
# Connect to Wattpilot:
ip = args.ip or os.environ.get('WATTPILOT_HOST')
password = args.password or os.environ.get('WATTPILOT_PASSWORD')
wp = wattpilot.Wattpilot(ip,password)

# Enable MQTT integration:
if MQTT_ENABLED == "true":
_LOGGER.debug(f"Registering message callback for MQTT integration.")
wp.register_message_callback(mqtt_message)
wp.connect()

# Wait for connection and initializon:
wait_timeout(lambda: wp.connected, WATTPILOT_CONNECT_TIMEOUT) or exit("ERROR: Timeout while connecting to Wattpilot!")
wait_timeout(lambda: wp.allPropsInitialized, WATTPILOT_INITIALIZED_TIMEOUT) or exit("ERROR: Timeout while waiting for property initialization!")

# Process commands:
if args.cmdline:
# Process commands passed on the commandline
process_command(wp,wpi,args.cmdline)
else:
# Start interactive shell
process_command(wp,wpi,"info")
exit=False
while not exit:
try:
command = input('> ')
except EOFError as e:
command = "exit"
print()
exit = process_command(wp,wpi,command)


if __name__ == '__main__':
main()

0 comments on commit ca98bb3

Please sign in to comment.