Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Target v3 Alpha - changes to restructure to make tests more resilient to support HA #80

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ name: Build & Test

on:
push:
branches: [ "master" ]
branches: [ "main" ]
pull_request:
branches: [ "master" ]
branches: [ "main" ]

jobs:
build:
Expand Down Expand Up @@ -35,6 +35,6 @@ jobs:
poetry run flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
poetry run flake8 . --count --exit-zero --max-complexity=12 --max-line-length=127 --statistics
- name: Test with pytest
- name: Test with pytest and generate terminal coverage report
run: |
poetry run pytest
poetry run pytest --cov-report term --cov=heatmiserv3 tests/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ test.py
build/
dist/
.pytest_cache/v/cache/lastfailed
coverage.xml
3 changes: 1 addition & 2 deletions heatmiserv3/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,4 @@ prt:
60: Weekend
61: Weekend
62: Weekend
63: Weekend
Responses:
63: Weekend
60 changes: 10 additions & 50 deletions heatmiserv3/connection.py
Original file line number Diff line number Diff line change
@@ -1,72 +1,32 @@
"""This module is effectively a singleton for serial comms"""
import serial
import logging
from . import constants
from heatmiserv3 import heatmiser

logging.basicConfig(level=logging.INFO)


class HeatmiserUH1(object):
"""
Represents the UH1 interface that holds the serial
connection, and can have multiple thermostats
connection, and can manage up to 32 devices.
"""

def __init__(self, ipaddress, port):
self.thermostats = {}
self._serport = serial.serial_for_url("socket://" + ipaddress + ":" + port)
def __init__(self, serialport):

self.serialport = serialport
# Ensures that the serial port has not
# been left hanging around by a previous process.
serport_response = self._serport.close()
logging.info("SerialPortResponse: %s", serport_response)
self._serport.baudrate = constants.COM_BAUD
self._serport.bytesize = constants.COM_SIZE
self._serport.parity = constants.COM_PARITY
self._serport.stopbits = constants.COM_STOP
self._serport.timeout = constants.COM_TIMEOUT
self.status = False
self.serialport.close()
self._open()

def _open(self):
if not self.status:
if not self.serialport.is_open:
logging.info("Opening serial port.")
self._serport.open()
self.status = True
return True
self.serialport.open()
else:
logging.info("Attempting to access already open port")
return False


def reopen(self):
if not self.status:
self._serport.open()
self.status = True
return self.status
else:
logging.error("Cannot open serial port")
self.serialport.open()

def __del__(self):
logging.info("Closing serial port.")
self._serport.close()

def registerThermostat(self, thermostat):
"""Registers a thermostat with the UH1"""
try:
type(thermostat) == heatmiser.HeatmiserThermostat
if thermostat.address in self.thermostats.keys():
raise ValueError("Key already present")
else:
self.thermostats[thermostat.address] = thermostat
except ValueError:
pass
except Exception as e:
logging.info("You're not adding a HeatmiiserThermostat Object")
logging.info(e.message)
return self._serport

def listThermostats(self):
if self.thermostats:
return self.thermostats
else:
return None
self.serialport.close()
79 changes: 79 additions & 0 deletions heatmiserv3/crc16.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@

from . import constants

# Believe this is known as CCITT (0xFFFF)
# This is the CRC function converted directly from the Heatmiser C code
# provided in their API

class CRC16:
"""This is the CRC hashing mechanism used by the V3 protocol."""

LookupHigh = [
0x00,
0x10,
0x20,
0x30,
0x40,
0x50,
0x60,
0x70,
0x81,
0x91,
0xA1,
0xB1,
0xC1,
0xD1,
0xE1,
0xF1,
]
LookupLow = [
0x00,
0x21,
0x42,
0x63,
0x84,
0xA5,
0xC6,
0xE7,
0x08,
0x29,
0x4A,
0x6B,
0x8C,
0xAD,
0xCE,
0xEF,
]

def __init__(self):
self.high = constants.BYTEMASK
self.low = constants.BYTEMASK

def extract_bits(self, val):
"""Extras the 4 bits, XORS the message data, and does table lookups."""
# Step one, extract the Most significant 4 bits of the CRC register
thisval = self.high >> 4
# XOR in the Message Data into the extracted bits
thisval = thisval ^ val
# Shift the CRC Register left 4 bits
self.high = (self.high << 4) | (self.low >> 4)
self.high = self.high & constants.BYTEMASK # force char
self.low = self.low << 4
self.low = self.low & constants.BYTEMASK # force char
# Do the table lookups and XOR the result into the CRC tables
self.high = self.high ^ self.LookupHigh[thisval]
self.high = self.high & constants.BYTEMASK # force char
self.low = self.low ^ self.LookupLow[thisval]
self.low = self.low & constants.BYTEMASK # force char

def update(self, val):
"""Updates the CRC value using bitwise operations."""
self.extract_bits(val >> 4) # High nibble first
self.extract_bits(val & 0x0F) # Low nibble

def run(self, message):
"""Calculates a CRC"""
for value in message:
self.update(value)
return [self.low, self.high]

8 changes: 8 additions & 0 deletions heatmiserv3/formats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import struct

commandFrameMessage = ">I"

commandFrameReply = ">"


message = struct.Struct('I I I I I I I I I I I')
Loading
Loading