Skip to content

Commit

Permalink
Merge pull request #115 from MartinHjelmare/0.12
Browse files Browse the repository at this point in the history
0.12
  • Loading branch information
MartinHjelmare authored Jan 14, 2018
2 parents 0072f30 + 9a60152 commit 97f7b4f
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 53 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
# Change Log

## [0.12.0](https://github.com/theolind/pymysensors/tree/0.12.0) (2018-01-14)
[Full Changelog](https://github.com/theolind/pymysensors/compare/0.11.1...0.12.0)

**Closed issues:**

- Problem after pull \#102 [\#109](https://github.com/theolind/pymysensors/issues/109)

**Merged pull requests:**

- Upgrade lint and test requirements [\#114](https://github.com/theolind/pymysensors/pull/114) ([MartinHjelmare](https://github.com/MartinHjelmare))
- Dump JSON file with indentation [\#113](https://github.com/theolind/pymysensors/pull/113) ([Mirodin](https://github.com/Mirodin))
- Fix reboot not returning to False [\#111](https://github.com/theolind/pymysensors/pull/111) ([MartinHjelmare](https://github.com/MartinHjelmare))

## [0.11.1](https://github.com/theolind/pymysensors/tree/0.11.1) (2017-08-29)
[Full Changelog](https://github.com/theolind/pymysensors/compare/0.11...0.11.1)

**Merged pull requests:**

- 0.11.1 [\#110](https://github.com/theolind/pymysensors/pull/110) ([MartinHjelmare](https://github.com/MartinHjelmare))
- Fix protocol version null and enhance validation [\#108](https://github.com/theolind/pymysensors/pull/108) ([MartinHjelmare](https://github.com/MartinHjelmare))
- Update release instructions [\#107](https://github.com/theolind/pymysensors/pull/107) ([MartinHjelmare](https://github.com/MartinHjelmare))

## [0.11](https://github.com/theolind/pymysensors/tree/0.11) (2017-08-21)
[Full Changelog](https://github.com/theolind/pymysensors/compare/0.10...0.11)
Expand Down
16 changes: 9 additions & 7 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,28 @@
pypi
test

[test]
repository = https://testpypi.python.org/pypi
[testpypi]
repository = https://test.pypi.org/legacy/
username = username
password = password

[pypi]
repository = https://pypi.python.org/pypi
username = username
password = password
```

There's a new API for PyPi, see this [page](https://packaging.python.org/guides/migrating-to-pypi-org/#uploading) for more info.
- Install `twine` v1.8.0+.
```
pip3 install --upgrade twine
```
- Create a release branch from dev.
- Merge master into the release branch to make the PR mergeable.
- Update version in `mysensors/version.py` to the new version number, eg `'0.2.0'`.
- Update `CHANGELOG.md` by running `scripts/gen_changelog`.
- Commit and push the release branch.
- Create a pull request from release branch to master with the upcoming release number as the title. Put the changes for the new release from the updated changelog as the PR message.
- Merge the pull request into master, do not squash.
- Go to github releases and tag a new release on the master branch.
- Go to github releases and tag a new release on the master branch. Put the PR message as the description for the release.
- Fetch and checkout the master branch.
- Generate `README.rst` by running `scripts/gen_rst` (pandoc needed).
- Build source and wheel distributions:
Expand All @@ -35,9 +38,8 @@
rm -rf dist
python3 setup.py sdist bdist_wheel
```
- Stage release: `twine upload -r test dist/*`
- Stage release: `twine upload -r testpypi dist/*`
- Release: `twine upload -r pypi dist/*`
- Fetch and checkout the master branch.
- Fetch and checkout the develop branch.
- Merge master into develop.
- Update version in `mysensors/version.py` to the new develop version number, eg `'0.3.0.dev0'`
Expand Down
75 changes: 42 additions & 33 deletions mysensors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,29 +74,30 @@ def _handle_presentation(self, msg):
# this is a presentation of the sensor platform
sensorid = self.add_sensor(msg.node_id)
if sensorid is None:
return
if msg.node_id in self.sensors:
self.sensors[msg.node_id].reboot = False
return None
self.sensors[msg.node_id].type = msg.sub_type
self.sensors[msg.node_id].protocol_version = msg.payload
self.sensors[msg.node_id].reboot = False
self.alert(msg)
return msg
else:
# this is a presentation of a child sensor
if not self.is_sensor(msg.node_id):
_LOGGER.error('Node %s is unknown, will not add child %s.',
msg.node_id, msg.child_id)
return
return None
child_id = self.sensors[msg.node_id].add_child_sensor(
msg.child_id, msg.sub_type, msg.payload)
if child_id is None:
return
return None
self.alert(msg)
return msg

def _handle_set(self, msg):
"""Process a set message."""
if not self.is_sensor(msg.node_id, msg.child_id):
return
return None
self.sensors[msg.node_id].set_child_value(
msg.child_id, msg.sub_type, msg.payload)
if self.sensors[msg.node_id].new_state:
Expand All @@ -109,19 +110,22 @@ def _handle_set(self, msg):
return msg.modify(
child_id=SYSTEM_CHILD_ID, type=self.const.MessageType.internal,
ack=0, sub_type=self.const.Internal.I_REBOOT, payload='')
return None

def _handle_req(self, msg):
"""Process a req message.
This will return the value if it exists. If no value exists,
nothing is returned.
"""
if self.is_sensor(msg.node_id, msg.child_id):
value = self.sensors[msg.node_id].children[
msg.child_id].values.get(msg.sub_type)
if value is not None:
return msg.modify(
type=self.const.MessageType.set, payload=value)
if not self.is_sensor(msg.node_id, msg.child_id):
return None
value = self.sensors[msg.node_id].children[
msg.child_id].values.get(msg.sub_type)
if value is not None:
return msg.modify(
type=self.const.MessageType.set, payload=value)
return None

def _handle_heartbeat(self, msg):
"""Process a heartbeat message."""
Expand Down Expand Up @@ -151,10 +155,8 @@ def _handle_internal(self, msg):
elif msg.sub_type == self.const.Internal.I_TIME:
return msg.modify(ack=0, payload=calendar.timegm(time.localtime()))
actions = self.const.HANDLE_INTERNAL.get(msg.sub_type)
if not actions:
return
if actions.get('is_sensor') and not self.is_sensor(msg.node_id):
return
return None
if actions.get('setattr'):
setattr(self.sensors[msg.node_id], actions['setattr'], msg.payload)
if actions.get('fun'):
Expand All @@ -168,15 +170,17 @@ def _handle_internal(self, msg):
msg.payload)
if actions.get('msg'):
return msg.modify(**actions['msg'])
return None

def _handle_stream(self, msg):
"""Process a stream type message."""
if not self.is_sensor(msg.node_id):
return
return None
if msg.sub_type == self.const.Stream.ST_FIRMWARE_CONFIG_REQUEST:
return self.ota.respond_fw_config(msg)
elif msg.sub_type == self.const.Stream.ST_FIRMWARE_REQUEST:
return self.ota.respond_fw(msg)
return None

def send(self, message):
"""Implement this method in a child class."""
Expand All @@ -194,7 +198,7 @@ def logic(self, data):
msg.validate(self.protocol_version)
except (ValueError, vol.Invalid) as exc:
_LOGGER.warning('Not a valid message: %s', exc)
return
return None

if msg.type == self.const.MessageType.presentation:
ret = self._handle_presentation(msg)
Expand Down Expand Up @@ -225,7 +229,8 @@ def _load_pickle(self, filename):
def _save_json(self, filename):
"""Save sensors to json file."""
with open(filename, 'w') as file_handle:
json.dump(self.sensors, file_handle, cls=MySensorsJSONEncoder)
json.dump(self.sensors, file_handle, cls=MySensorsJSONEncoder,
indent=4)
file_handle.flush()
os.fsync(file_handle.fileno())

Expand Down Expand Up @@ -321,6 +326,7 @@ def _get_next_id(self):
next_id = 1
if next_id <= self.const.MAX_NODE_ID:
return next_id
return None

def add_sensor(self, sensorid=None):
"""Add a sensor to the gateway."""
Expand All @@ -329,6 +335,7 @@ def add_sensor(self, sensorid=None):
if sensorid is not None and sensorid not in self.sensors:
self.sensors[sensorid] = Sensor(sensorid)
return sensorid
return None

def is_sensor(self, sensorid, child_id=None):
"""Return True if a sensor and its child exist."""
Expand All @@ -353,12 +360,13 @@ def is_sensor(self, sensorid, child_id=None):
def _route_message(self, msg):
if not isinstance(msg, Message) or \
msg.type == self.const.MessageType.presentation:
return
return None
if (msg.node_id not in self.sensors or
msg.type == self.const.MessageType.stream or
not self.sensors[msg.node_id].new_state):
return msg
self.sensors[msg.node_id].queue.append(msg.encode())
return None

def handle_queue(self, queue=None):
"""Handle queue.
Expand All @@ -368,17 +376,18 @@ def handle_queue(self, queue=None):
"""
if queue is None:
queue = self.queue
if not queue.empty():
start = timer()
func, args, kwargs = queue.get()
reply = func(*args, **kwargs)
queue.task_done()
end = timer()
if end - start > 0.1:
_LOGGER.debug(
'Handle queue with call %s(%s, %s) took %.3f seconds',
func, args, kwargs, end - start)
return reply
if queue.empty():
return None
start = timer()
func, args, kwargs = queue.get()
reply = func(*args, **kwargs)
queue.task_done()
end = timer()
if end - start > 0.1:
_LOGGER.debug(
'Handle queue with call %s(%s, %s) took %.3f seconds',
func, args, kwargs, end - start)
return reply

def fill_queue(self, func, args=None, kwargs=None, queue=None):
"""Put a function in a queue.
Expand Down Expand Up @@ -492,7 +501,7 @@ def add_child_sensor(self, child_id, child_type, description=''):
_LOGGER.warning(
'child_id %s already exists in children of node %s, '
'cannot add child', child_id, self.sensor_id)
return
return None
self.children[child_id] = ChildSensor(
child_id, child_type, description)
return child_id
Expand All @@ -501,7 +510,7 @@ def set_child_value(self, child_id, value_type, value, **kwargs):
"""Set a child sensor's value."""
children = kwargs.get('children', self.children)
if not isinstance(children, dict) or child_id not in children:
return
return None
msg_type = kwargs.get('msg_type', 1)
ack = kwargs.get('ack', 0)
msg = Message().modify(
Expand All @@ -513,13 +522,13 @@ def set_child_value(self, child_id, value_type, value, **kwargs):
'Not a valid message: node %s, child %s, type %s, ack %s, '
'sub_type %s, payload %s',
self.sensor_id, child_id, msg_type, ack, value_type, value)
return
return None
try:
msg = Message(msg_string)
msg.validate(self.protocol_version)
except (ValueError, AttributeError, vol.Invalid) as exc:
_LOGGER.error('Not a valid message: %s', exc)
return
return None
child = children[msg.child_id]
child.values[msg.sub_type] = msg.payload
return msg_string
Expand Down
2 changes: 1 addition & 1 deletion mysensors/gateway_mqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def _parse_mqtt_to_message(self, topic, payload, qos):
prefix_end_idx = topic.find('/'.join(not_prefix)) - 1
prefix = topic[:prefix_end_idx]
if prefix != self._in_prefix:
return
return None
if qos and qos > 0:
ack = '1'
else:
Expand Down
4 changes: 2 additions & 2 deletions mysensors/gateway_tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ def _check_connection(self):
def _handle_internal(self, msg):
if msg.sub_type == self.const.Internal.I_VERSION:
self.tcp_disconnect_timer = time.time()
else:
return super()._handle_internal(msg)
return None
return super()._handle_internal(msg)

def connect(self):
"""Connect to the socket object, on host and port."""
Expand Down
6 changes: 3 additions & 3 deletions mysensors/ota.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def check_fw(path):
bin_string = intel_hex.tobinstr()
except (IntelHexError, TypeError, ValueError) as exception:
_LOGGER.error(exception)
return
return None
pads = len(bin_string) % 128 # 128 bytes per page for atmega328
for _ in range(128 - pads): # pad up to even 128 bytes
bin_string += b'\xff'
Expand Down Expand Up @@ -106,7 +106,7 @@ def respond_fw(self, msg):
fw_type, fw_ver, fware = self._get_fw(
msg, (self.unstarted, self.started), req_fw_type, req_fw_ver)
if fware is None:
return
return None
blk_data = fware['data'][
req_blk * FIRMWARE_BLOCK_SIZE:
req_blk * FIRMWARE_BLOCK_SIZE + FIRMWARE_BLOCK_SIZE]
Expand All @@ -130,7 +130,7 @@ def respond_fw_config(self, msg):
fw_type, fw_ver, fware = self._get_fw(
msg, (self.requested, self.unstarted))
if fware is None:
return
return None
if fw_type != req_fw_type:
_LOGGER.warning(
'Firmware type %s of update is not identical to existing '
Expand Down
4 changes: 2 additions & 2 deletions mysensors/version.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Store version constants."""
MAJOR_VERSION = 0
MINOR_VERSION = 11
PATCH_VERSION = 1
MINOR_VERSION = 12
PATCH_VERSION = '0'
__version__ = '{}.{}.{}'.format(MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION)
10 changes: 5 additions & 5 deletions requirements_test.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
flake8==3.4.1
pylint==1.7.2
pytest==3.2.0
flake8==3.5.0
pylint==1.8.1
pytest==3.3.2
pytest-cov==2.5.1
pytest-timeout==1.2.0
pydocstyle==2.0.0
pytest-timeout==1.2.1
pydocstyle==2.1.1
2 changes: 2 additions & 0 deletions tests/test_mysensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,8 @@ def test_set_and_reboot(self):
sensor.reboot = True
ret = self.gateway.logic('1;0;1;0;23;43\n')
self.assertEqual(ret, '1;255;3;0;13;\n')
self.gateway.logic('1;255;0;0;17;1.4.1\n')
self.assertEqual(sensor.reboot, False)

def test_set_child_value(self):
"""Test Gateway method set_child_value."""
Expand Down

0 comments on commit 97f7b4f

Please sign in to comment.