Skip to content

Commit

Permalink
working?
Browse files Browse the repository at this point in the history
  • Loading branch information
jantman committed Oct 24, 2023
1 parent 9f4ff0c commit 63a0fe3
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 73 deletions.
32 changes: 27 additions & 5 deletions py_vista_turbo_serial/communicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@
"""

import logging
from typing import List, Generator
from typing import List, Generator, Dict

from serial import Serial

from py_vista_turbo_serial.messages import MessagePacket
from py_vista_turbo_serial.messages import (
MessagePacket, ArmingStatusRequest, ZoneStatusRequest,
ZoneDescriptorRequest, ZoneDescriptorReport
)

logger = logging.getLogger(__name__)

Expand All @@ -57,7 +60,12 @@ def __init__(self, port: str, timeout_sec: int = 1):
port, baudrate=9600, timeout=timeout_sec
) # default 8N1
logger.debug('Serial is connected')
self.outgoing: List[str] = []
self.zones: Dict[int, str] = {}
self.outgoing: List[str] = [
ArmingStatusRequest.generate(),
ZoneDescriptorRequest.generate(),
ZoneStatusRequest.generate(),
]

def __del__(self):
logger.debug('Closing serial port')
Expand All @@ -76,14 +84,28 @@ def communicate(self) -> Generator[MessagePacket, None, None]:
logger.info('Sending message: %s', msg)
self.serial.write(bytes(msg, 'ascii') + b'\r\n')
# this might be better with select(), but let's try this...
asked_for_zones: bool = False
have_zones: bool = False
zone_msg: str = ZoneDescriptorRequest.generate()
while True:
# @TODO handle exception on timeout
line = self.serial.readline().decode().strip()
if line == '':
continue
logger.debug('Got line: %s', line)
yield MessagePacket.parse(line)
if self.outgoing:
pkt: MessagePacket = MessagePacket.parse(line, self.zones)
if isinstance(pkt, ZoneDescriptorReport):
self.zones[pkt.zone_num] = pkt.zone_name
if pkt.zone_num == 0:
have_zones = True
logger.info('Zones: %s', self.zones)
yield pkt
if (
self.outgoing and
((not asked_for_zones) or (asked_for_zones and have_zones))
):
msg = self.outgoing.pop(0)
logger.info('Sending message: %s', msg)
self.serial.write(bytes(msg, 'ascii') + b'\r\n')
if msg == zone_msg:
asked_for_zones = True
34 changes: 27 additions & 7 deletions py_vista_turbo_serial/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,49 @@
##################################################################################
"""

from typing import Union
from typing import Union, Dict, Optional

EventTypes: type = Union['SystemEvent', 'UnknownEvent']


def subclasses_recursive(cls):
direct = cls.__subclasses__()
indirect = []
for subclass in direct:
indirect.extend(subclasses_recursive(subclass))
return direct + indirect


class SystemEvent:

NAME: str = 'Unknown Event'

CODE: int = 0

def __init__(self, zone_or_user: int):
IS_ZONE: bool = True

def __init__(self, zone_or_user: int, zone_or_user_name: Optional[str]):
self.zone_or_user: int = zone_or_user
self.zone_or_user_name: Optional[str] = zone_or_user_name

def __repr__(self) -> str:
return f'<{self.NAME}(zone_or_user={self.zone_or_user})>'
if not self.IS_ZONE:
return f'<{self.NAME}(user={self.zone_or_user})>'
if self.zone_or_user_name is None:
return f'<{self.NAME}(zone_num={self.zone_or_user})>'
return (f'<{self.NAME}(zone={self.zone_or_user} '
f'"{self.zone_or_user_name}")>')

@classmethod
def event_for_code(
cls, event_code: int, zone_or_user: int
cls, event_code: int, zone_or_user: int, zones: Dict[int, str]
) -> EventTypes:
for klass in cls.__subclasses__():
for klass in subclasses_recursive(cls):
if klass.CODE == event_code:
return klass(zone_or_user)
if klass.IS_ZONE:
return klass(zone_or_user, zones.get(zone_or_user))
else:
return klass(zone_or_user, None)
return UnknownEvent(event_code, zone_or_user)


Expand Down Expand Up @@ -144,7 +163,7 @@ class OtherTroubleRestore(SystemEvent):


class ArmDisarmEvent(SystemEvent):
pass
IS_ZONE: bool = False


class ArmStay(ArmDisarmEvent):
Expand Down Expand Up @@ -185,6 +204,7 @@ class AcRestore(SystemEvent):
class AlarmCancel(SystemEvent):
NAME: str = 'Alarm Cancel'
CODE: int = 0x20
IS_ZONE: bool = False


class OtherBypass(SystemEvent):
Expand Down
24 changes: 12 additions & 12 deletions py_vista_turbo_serial/examples/pushover_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@
from datetime import datetime

from py_vista_turbo_serial.communicator import Communicator
from py_vista_turbo_serial.messages import (
MessagePacket, ArmingStatusRequest, ZoneStatusRequest
)
from py_vista_turbo_serial.messages import MessagePacket

import requests

Expand Down Expand Up @@ -94,6 +92,9 @@ def __init__(

def _do_notify_pushover(self, title, message, sound=None):
"""Build Pushover API request arguments and call _send_pushover"""
if self._dry_run:
logger.warning('DRY RUN - don\'t actually send')
return
d = {
'data': {
'token': self._app_token,
Expand All @@ -106,9 +107,6 @@ def _do_notify_pushover(self, title, message, sound=None):
if sound is not None:
d['data']['sound'] = sound
logger.info('Sending Pushover notification: %s', d)
if self._dry_run:
logger.warning('DRY RUN - don\'t actually send')
return
for i in range(0, 2):
try:
self._send_pushover(d)
Expand Down Expand Up @@ -183,6 +181,7 @@ def __init__(
'PushoverAlarmNotifier initializing at ' +
datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
)
self._dry_run: bool = dry_run
self.notifier: PushoverNotifier = PushoverNotifier(
store=self.store,
app_token=os.environ['PUSHOVER_APIKEY'],
Expand All @@ -198,8 +197,6 @@ def run(self):
'PushoverAlarmNotifier starting run loop at ' +
datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
)
self.panel.send_message(ArmingStatusRequest.generate())
self.panel.send_message(ZoneStatusRequest.generate())
message: MessagePacket
dt: str
for message in self.panel.communicate():
Expand All @@ -212,6 +209,9 @@ def run(self):
else:
self.store.add(str(message) + ' (' + dt + ')')
"""
if self._dry_run:
logger.info(str(message))
continue
self.store.add(str(message) + ' (' + dt + ')')


Expand Down Expand Up @@ -239,19 +239,19 @@ def parse_args(argv):
return args


def set_log_info(l: logging.Logger):
def set_log_info(lgr: logging.Logger):
"""set logger level to INFO"""
set_log_level_format(
l,
lgr,
logging.INFO,
'%(asctime)s %(levelname)s:%(name)s:%(message)s'
)


def set_log_debug(l: logging.Logger):
def set_log_debug(lgr: logging.Logger):
"""set logger level to DEBUG, and debug-level output format"""
set_log_level_format(
l,
lgr,
logging.DEBUG,
"%(asctime)s [%(levelname)s %(filename)s:%(lineno)s - "
"%(name)s.%(funcName)s() ] %(message)s"
Expand Down
Loading

0 comments on commit 63a0fe3

Please sign in to comment.